CompletionService是Java8的新增接口,JDK为其提供了一个实现类ExecutorCompletionService。这个类是为线程池中Task的执行结果服务的,即为Executor中Task返回Future而服务的。CompletionService的实现目标是任务先完成可优先获取到,即结果按照完成先后顺序排序。
1、成员变量
CompletionService的使用非常简单。从源码查看ExecutorCompletionService类,该类只有三个成员变量:
public class ExecutorCompletionService<V> implements CompletionService<V> {
private final Executor executor;
private final AbstractExecutorService aes;
private final BlockingQueue<Future<V>> completionQueue;
...
}
可以看到ExecutorCompletionService主要是增强executor线程池的。Task包装后被塞入completionQueue,当Task结束,其Future就可以从completionQueue中获取到。
其基本原理可以参看下图:
2、示例代码
下面分别使用自己写集合和CompletionService来实现接收线程池中的返回结果
public class CompletionServiceClient {
private final int POOL_SIZE = 8; //线程池大小
private final int TOTAL_TASK = 8; //总任务数量
// 方法一,自己写集合来实现获取线程池中任务的返回结果
public void testByQueue() throws Exception {
long start = System.currentTimeMillis();
//统计所有任务休眠的总时长
AtomicInteger count = new AtomicInteger(0);
// 创建线程池
ExecutorService pool = Executors.newFixedThreadPool(POOL_SIZE);
//容器存放提交给线程池的任务,list,map,
BlockingQueue<Future<Integer>> queue = new LinkedBlockingQueue<Future<Integer>>();
// 向里面扔任务
for (int i = 0; i < TOTAL_TASK; i++) {
Future<Integer> future = pool.submit(new WorkTask("ExecTask" + i));
queue.add(future);//i=0 先进队列,i=1的任务跟着进
}
// 检查线程池任务执行结果
for (int i = 0; i < TOTAL_TASK; i++) {
int sleptTime = queue.take().get();///i=0先取到,i=1的后取到
System.out.println(" slept "+sleptTime+" ms ...");
count.addAndGet(sleptTime);
}
// 关闭线程池
pool.shutdown();
System.out.println("-------------tasks sleep time "+count.get()
+"ms,and spend time "
+(System.currentTimeMillis()-start)+" ms");
}
// 方法二,通过CompletionService来实现获取线程池中任务的返回结果
public void testByCompletion() throws Exception {
long start = System.currentTimeMillis();
AtomicInteger count = new AtomicInteger(0);
// 创建线程池
ExecutorService pool = Executors.newFixedThreadPool(POOL_SIZE);
CompletionService<Integer> cService = new ExecutorCompletionService<>(pool);
// 向里面扔任务
for (int i = 0; i < TOTAL_TASK; i++) {
cService.submit(new WorkTask("ExecTask" + i));
}
// 检查线程池任务执行结果
for (int i = 0; i < TOTAL_TASK; i++) {
int sleptTime = cService.take().get();
System.out.println(" slept "+sleptTime+" ms ...");
count.addAndGet(sleptTime);
}
// 关闭线程池
pool.shutdown();
System.out.println("-------------tasks sleep time "+count.get()
+"ms,and spend time "
+(System.currentTimeMillis()-start)+" ms");
}
//任务类
public class WorkTask implements Callable<Integer> {
private String name;
public WorkTask(String name) {
this.name = name;
}
@Override
public Integer call() {
int sleepTime = new Random().nextInt(1000);
try {
Thread.sleep(sleepTime);
} catch (InterruptedException e) {
e.printStackTrace();
}
// 返回给调用者的值
return sleepTime;
}
}
public static void main(String[] args) throws Exception {
CompletionServiceClient client = new CompletionServiceClient();
client.testByQueue();
client.testByCompletion();
}
}
输出结果:
slept 431 ms ...
slept 55 ms ...
slept 381 ms ...
slept 958 ms ...
slept 610 ms ...
slept 72 ms ...
slept 112 ms ...
slept 645 ms ...
-------------tasks sleep time 3264ms,and spend time 1011 ms
slept 134 ms ...
slept 237 ms ...
slept 240 ms ...
slept 248 ms ...
slept 342 ms ...
slept 634 ms ...
slept 784 ms ...
slept 834 ms ...
-------------tasks sleep time 3453ms,and spend time 836 ms
由上面结果可以看出,通过自己封装的集合进行收集线程池返回值时,返回的结果是遵循LinkedBlockingQueue的先进后出的结果;而CompletionService会根据线程的执行快慢进行返回,执行时间越短的,返回时候越快。