任务提交到线程池后,将任务的Future放到一个List中,然后遍历List,通过Future的get方法得到返回值。如果在遍历过程中get方法阻塞,即使位于List后面的Future已经完成,遍历List的线程也要继续等待,这就对效率有比较大的影响。我们希望任务结束后,返回值能够立即被获取,而不是要等待其他任务结束。
CompletionService正是为此而生,实现中通过维护一个队列保存结束任务的Future,如果有任务结束,任务的Future会保存到队列中,从该队列中一定能拿到任务的返回结果。如果没有已经完成的任务,队列为空,取结果的线程会阻塞。
CompletionService
我们先看下接口CompletionService
public interface CompletionService<V> {
//提交任务
Future<V> submit(Callable<V> task);
//提交任务
Future<V> submit(Runnable task, V result);
//取下一个已经结束任务的返回值,如果没有则等待
//注意,该方法响应中断
Future<V> take() throws InterruptedException;
//取下一个已经结束任务的返回值,如果没有返回null
Future<V> poll();
//取下一个已经结束任务的返回值,如果没有最多等待timeout时间
Future<V> poll(long timeout, TimeUnit unit) throws InterruptedException;
}
该接口很简单,主要就是take、pool方法,取已经完成任务的返回值,接着我们看下jdk中该接口的实现类,ExecutorCompletionService。
ExecutorCompletionService
该类实现了CompletionService,维护一个阻塞队列(默认为LinkedBlockingQueue)保存已经完成的任务Future。worker执行完某个任务时会将任务的Future添加到该阻塞队列,阻塞队列按任务的完成顺序保存了任务的Future。取任务时,如果阻塞队列为空,说明没有已经完成的任务。看下该该类的实现:
public class ExecutorCompletionService<V> implements CompletionService<V> {
//任务的执行委托给了executor
private final Executor executor;
//如果executor继承自AbstractExecutorService,aes和executor指向同一个对象
//否则aes为空
private final AbstractExecutorService aes;
//保存完成任务的Future
private final BlockingQueue<Future<V>> completionQueue;
//CompletionService执行的任务继承了FutureTask,重写了done方法
//每当任务结束后将任务的Future加到completionQueue中
private class QueueingFuture extends FutureTask<Void> {
QueueingFuture(RunnableFuture<V> task) {
super(task, null);
this.task = task;
}
//任务结束后将Future加到队列中
protected void done() { completionQueue.add(task); }
private final Future<V> task;
}
//包装成FutureTask
private RunnableFuture<V> newTaskFor(Callable<V> task) {
if (aes == null)
return new FutureTask<V>(task);
else
return aes.newTaskFor(task);
}
//包装成FutureTask
private RunnableFuture<V> newTaskFor(Runnable task, V result) {
if (aes == null)
return new FutureTask<V>(task, result);
else
return aes.newTaskFor(task, result);
}
//构造函数,指定执行器executor
public ExecutorCompletionService(Executor executor) {
if (executor == null)
throw new NullPointerException();
this.executor = executor;
this.aes = (executor instanceof AbstractExecutorService) ?
(AbstractExecutorService) executor : null;
//用LinkedBlockingQueue保存完成任务的Future
this.completionQueue = new LinkedBlockingQueue<Future<V>>();
}
//构造函数,指定executor和queue
public ExecutorCompletionService(Executor executor,
BlockingQueue<Future<V>> completionQueue) {
if (executor == null || completionQueue == null)
throw new NullPointerException();
this.executor = executor;
this.aes = (executor instanceof AbstractExecutorService) ?
(AbstractExecutorService) executor : null;
this.completionQueue = completionQueue;
}
//提交任务,将任务包装成QueueingFuture后交给executor执行
public Future<V> submit(Callable<V> task) {
if (task == null) throw new NullPointerException();
RunnableFuture<V> f = newTaskFor(task);
executor.execute(new QueueingFuture(f));
return f;
}
public Future<V> submit(Runnable task, V result) {
if (task == null) throw new NullPointerException();
RunnableFuture<V> f = newTaskFor(task, result);
executor.execute(new QueueingFuture(f));
return f;
}
//从队列中取已经结束任务的返回值,如果队列为空则阻塞
public Future<V> take() throws InterruptedException {
return completionQueue.take();
}
//从队列中取已经结束任务的返回值,如果队列为空返回null
public Future<V> poll() {
return completionQueue.poll();
}
//从队列中取已经结束任务的返回值,如果超时,返回空
public Future<V> poll(long timeout, TimeUnit unit)
throws InterruptedException {
return completionQueue.poll(timeout, unit);
}
}
ExecutorCompletionService实现也很简单,主要是QueueingFuture,将QueueingFuture作为任务来执行,QueueingFuture继承FutureTask,重写了done方法。done方法在FutureTask中是一个空方法,留给子类重写。任务完成后会调用done方法,将任务的Future加到队列中。