作用:
可以看做是线程池Executor和阻塞队列BlockingQueue的结合体,Executor用来执行任务,BlockingQueue用来存放线程池执行的有返回结果的任务(即实现Callable的任务),通过BlockingQueue的put和take获取任务的执行结果
构造理解
public ExecutorCompletionService(Executor executor) {
if (executor == null)
throw new NullPointerException();
//执行任务的线程池
this.executor = executor;
this.aes = (executor instanceof AbstractExecutorService) ?
(AbstractExecutorService) executor : null;
//存放任务执行结果的队列
this.completionQueue = new LinkedBlockingQueue<Future<V>>();
}
CompletionService构造中会创建一个blockingQueue保存线程池的执行结果;
当一个任务提交到ExucutorCompletionService时,会包装成QueueingFuture(是FutureTask的一个子类)然后改写FutureTask的done方法,也就是执行完成之后把Executor执行的计算结果放到blockingQueue中
优点
多个有返回结果的任务执行的时候,先执行完的会先到blockingQueue中,所以能够较少不必要的等待时间
示例对比:执行多个有返回结果的任务,普通方法和使用CompletionSErvice对比
public class Test {
static class Home implements Callable {
@Override
public Integer call() throws Exception {
//每个任务的执行时间又长又短,后面的任务可能比前面的任务优先执行
int sleepTime = new Random().nextInt(1000);
try {
Thread.sleep(sleepTime);
} catch (InterruptedException e) {
e.printStackTrace();
}
return sleepTime;
}
}
//普通方法,线程池+阻塞队列
static public void normalWay() throws InterruptedException, ExecutionException {
long startTime = System.currentTimeMillis();
AtomicInteger sum = new AtomicInteger(0);
ExecutorService pool = Executors.newFixedThreadPool(10);
//通过阻塞队列拿取结果
BlockingDeque<Future> blockingDeque = new LinkedBlockingDeque();
for (int i = 0; i < 10; i++) {
Future submit = pool.submit(new Home());
blockingDeque.add(submit);
}
for (int i = 0; i < 10; i++) {
sum.addAndGet((Integer)blockingDeque.take().get());
}
System.out.println("blockingDeque总共执行完毕花费时间:"+(System.currentTimeMillis()-startTime));
}
//使用CompletionsErvice
static public void useCompletionService() throws InterruptedException, ExecutionException {
long startTime = System.currentTimeMillis();
AtomicInteger sum = new AtomicInteger(0);
ExecutorService pool = Executors.newFixedThreadPool(10);
CompletionService service = new ExecutorCompletionService(pool);
for (int i = 0; i < 10; i++) {
service.submit(new Home());
}
for (int i = 0; i < 10; i++) {
sum.addAndGet((Integer) service.take().get());
}
System.out.println("useCompletionService总共执行完毕花费时间:"+(System.currentTimeMillis()-startTime));
}
public static void main(String[] args) throws ExecutionException, InterruptedException {
normalWay();
useCompletionService();
}
}
结果:使用CompletionService花费时间更少
普通方法:阻塞队列执行take方法的时候,后面的任务完成了,前面的任务没有完成,主线程就等待
CompletionService:先完成的任务结果会优先进入阻塞队列,所以能够节省时间
blockingDeque总共执行完毕花费时间:968
useCompletionService总共执行完毕花费时间:918