CompletionService简介
CompletionService将Executor和BlockingQueue的功能融合在一起,将Callable任务提交给CompletionService来执行,然后使用类似于队列操作的take和poll等方法来获得已完成的结果,而这些结果会在完成时被封装为Future。
CompletionService
实现了生产者提交任务和消费者获取结果的解耦,生产者和消费者都不用关心任务的完成顺序,由CompletionService
来保证,消费者一定是按照任务完成的先后顺序来获取执行结果。利用这个特性,我们可以轻松实现后续处理的有序性,避免无谓的等待。
另外,CompletionService 是接口,无法直接使用,通常使用他的实现类ExecutorCompletionService
应用场景
其主要解决的场景是主线程提交多个任务然后希望有任务完成就处理结果,并且按照任务完成顺序逐个处理(譬如并发请求返回刷新UI的操作就可以谁请求成功就开始刷而不用等待所有OK才刷等)
常用方法
- Future submit(Callabletask);
submit 用于提交一个 Callable 对象,用于提交一个可以获得结果的线程任务
- Future submit(Runnable task, V result);
submit 用于提交一个 Runnable 对象及 result 对象,类似于上面的 submit,但是 runnable 的返回值 void 无法获得线程的结果,所以添加了 result 用于做为参数的桥梁
- Future take() throws InterruptedException;
take 用于取出最新的线程执行结果,注意这里是阻塞的,直到可以拿到任务为止
- Future poll();
take 用于取出最新的线程执行结果,是非阻塞的,如果没有结果就返回 null
- Future poll(long timeout, TimeUnit unit) throws InterruptedException;
带超时时间等待的获取任务方法
应用案例:查询3个网站,汇总结果
package com.example.demo.juc;
import cn.hutool.core.thread.ThreadUtil;
import com.google.common.collect.Lists;
import java.util.List;
import java.util.concurrent.*;
/**
* @Author shangkaihui
* @Date 2020/8/13 09:44
* @Desc
*/
public class CompletionServiceDemo {
public static void main(String[] args) throws ExecutionException, InterruptedException {
// 查询信息
String queryName = "java";
// 调用查询接口
long startTime = System.currentTimeMillis();
queryInfoCode3(queryName);
System.out.println("耗时: " + (System.currentTimeMillis() - startTime));
}
/**
* 串行聚合查询信息 code 1
*
* @param queryName
* @return
*/
private static List<String> queryInfoCode1(String queryName) {
List<String> resultList = Lists.newArrayList();
String webA = searchWebA(queryName);
resultList.add(webA);
String webB = searchWebB(queryName);
resultList.add(webB);
String webC = searchWebC(queryName);
resultList.add(webC);
return resultList;
}
/**
* 使用多线程future并行聚合查询信息,任务完成时间取决于耗时最长的任务
*
* @param queryName
* @return
*/
private static List<String> queryInfoCode2(String queryName) throws ExecutionException, InterruptedException {
List<String> resultList = Lists.newArrayList();
// 创建3个线程的线程池
ExecutorService pool = Executors.newFixedThreadPool(3);
try {
// 创建任务的 feature
Future<String> webAFuture = pool.submit(() -> searchWebA(queryName));
Future<String> webBFuture = pool.submit(() -> searchWebB(queryName));
Future<String> webCFuture = pool.submit(() -> searchWebC(queryName));
// 得到任务结果
resultList.add(webAFuture.get());
resultList.add(webBFuture.get());
resultList.add(webCFuture.get());
} finally {
// 关闭线程池
pool.shutdown();
}
return resultList;
}
/**
* 使用CompletionService完成聚合查询信息,先完成的任务先返回
*
* @param queryName
* @return
*/
private static void queryInfoCode3(String queryName) throws ExecutionException, InterruptedException {
// 开始时间
long startTime = System.currentTimeMillis();
// 创建 CompletionService
ExecutorService executorService = Executors.newFixedThreadPool(3);
CompletionService executorCompletionService = new ExecutorCompletionService(executorService);
// 创建任务的 feature
executorCompletionService.submit(() -> searchWebA(queryName));
executorCompletionService.submit(() -> searchWebB(queryName));
executorCompletionService.submit(() -> searchWebC(queryName));
for (int i = 0; i < 3; i++) {
Future take = executorCompletionService.take();
System.out.println("获得请求结果 -> " + take.get());
System.out.println("通过 ws 推送给客户端,总共耗时" + (System.currentTimeMillis() - startTime));
}
executorService.shutdown();
}
/**
* 查询网站 A
*
* @param name
* @return
*/
public static String searchWebA(String name) {
ThreadUtil.sleep(5000);
return "webA";
}
/**
* 查询网站B
*
* @param name
* @return
*/
public static String searchWebB(String name) {
ThreadUtil.sleep(3000);
return "webB";
}
/**
* 查询网站C
*
* @param name
* @return
*/
public static String searchWebC(String name) {
ThreadUtil.sleep(500);
return "webC";
}
}