在我们日常使用线程池的时候,经常会有需要获得线程处理结果的时候。此时我们通常有两种做法。
《Java并发编程实践》一书6.3.5节CompletionService:Executor和BlockingQueue,有这样一段话:
"如果向Executor提交了一组计算任务,并且希望在计算完成后获得结果,那么可以保留与每个任务关联的Future,然后反复使用get方法,同时将参数timeout指定为0,从而通过轮询来判断任务是否完成。这种方法虽然可行,但却有些繁琐。幸运的是,还有一种更好的方法:完成服务CompletionService。"
这里通过代码比较一下:
package com.caojiulu;
import java.util.concurrent.*;
import java.util.concurrent.atomic.AtomicInteger;
/**
*@author caojilu
*
*类说明:
*/
public class CompletionCase {
private final int POOL_SIZE = Runtime.getRuntime().availableProcessors();
private final int TOTAL_TASK = Runtime.getRuntime().availableProcessors();
// 方法一,自己写集合来实现获取线程池中任务的返回结果
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 static void main(String[] args) throws Exception {
CompletionCase t = new CompletionCase();
t.testByQueue();
t.testByCompletion();
}
}
运行结果比较:
slept 541 ms ...
slept 883 ms ...
slept 890 ms ...
slept 436 ms ...
-------------tasks sleep time 2750ms,and spend time 902 ms
slept 81 ms ...
slept 294 ms ...
slept 364 ms ...
slept 374 ms ...
-------------tasks sleep time 1113ms,and spend time 376 ms
可以明显的看到,CompletionService要快很多。
所以如果需要通过线程池获取线程的返回结果时,请使用ExecutorCompletionService