作用
“既生 ExecutorService, 何生 CompletionService?” - SegmentFault 思否
案例
public static void main(String[] args) throws InterruptedException, ExecutionException {
ExecutorService executorService = Executors.newFixedThreadPool(4);
// ExecutorCompletionService 是 CompletionService 唯一实现类
CompletionService executorCompletionService= new ExecutorCompletionService<>(executorService);
List<Future<Integer>> futures = new ArrayList<Future<Integer>>();
futures.add(executorCompletionService.submit(()->{
TimeUnit.SECONDS.sleep(4);
return 1;
}));
futures.add(executorCompletionService.submit(()->{
TimeUnit.SECONDS.sleep(3);
return 2;
}));
futures.add(executorCompletionService.submit(()->{
TimeUnit.SECONDS.sleep(2);
return 3;
}));
futures.add(executorCompletionService.submit(()->{
TimeUnit.SECONDS.sleep(1);
return 4;
}));
// 遍历 Future list,通过 get() 方法获取每个 future 结果
for (int i=0; i<futures.size(); i++) {
Future<Integer> f = executorCompletionService.take();
Integer integer = f.get();
System.out.println(integer);
// 其他业务逻辑
}
}
//输出:
4
3
2
1
源码分析
主要属性
//执行任务的线程池
private final Executor executor;
//aes 主要是用于newTaskFor方法用于生成FutureTask对象,作用不是很大,可以为null
private final AbstractExecutorService aes;
//队列,主要用于存放完成了(done)的Future对象
private final BlockingQueue<Future<V>> completionQueue;
构造方法
// 传入一个线程池,负责执行任务
public ExecutorCompletionService(Executor executor) {
if (executor == null)
throw new NullPointerException();
this.executor = executor;
//这个aes属性作用不是很核心,可以忽略,可以为null,不影响使用
this.aes = (executor instanceof AbstractExecutorService) ?
(AbstractExecutorService) executor : null;
//队列默认是LinkedBlockingQueue
this.completionQueue = new LinkedBlockingQueue<Future<V>>();
}
//传入一个线程池,一个队列,队列不写默认是无界队列LinkedBlockingQueue
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;
}
ase属性的作用
private RunnableFuture<V> newTaskFor(Callable<V> task) {
//如果ase属性为null,就自己手动构造一个FutureTask
if (aes == null)
return new FutureTask<V>(task);
else
//不为空就调用aes的newTaskFor方法
return aes.newTaskFor(task);
}
private RunnableFuture<V> newTaskFor(Runnable task, V result) {
if (aes == null)
return new FutureTask<V>(task, result);
else return aes.newTaskFor(task, result);
}
内部类
//重写了FutureTask,主要是重写了done方法
private class QueueingFuture extends FutureTask<Void> {
QueueingFuture(RunnableFuture<V> task) {
super(task, null);
this.task = task;
}
//关键点设计:重写了done方法,done方法是任务完成后的回调。这里任务完成后会自动放入队列completionQueue
protected void done() { completionQueue.add(task); }
private final Future<V> task;
}
submit方法
public Future<V> submit(Callable<V> task) {
if (task == null) throw new NullPointerException();
RunnableFuture<V> f = newTaskFor(task);
//executor来真正干活,活生生的工具人
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;
}
获取完成的任务
//如果队列为空,会阻塞队列,是阻塞队列的api
public Future<V> take() throws InterruptedException {
return completionQueue.take();
}
//检索,删除队列的头。队列为空,则返回 null
public Future<V> poll() {
return completionQueue.poll();
}
//超时,返回null
public Future<V> poll(long timeout, TimeUnit unit)
throws InterruptedException {
return completionQueue.poll(timeout, unit);
}
设计特点
- 引入阻塞队列属性completionQueue 来存放完成的任务。
- 通过实现内部类QueueingFuture,重写done方法,实现任务完成回调,把完成的任务入队列。
- 通过以上,来实现先完成的任务先入队列。使用的时候,调用队列的take方法即可拿到先完成的任务。
思考
- 第一
这个ExecutorCompletionService一般是不能业务混用。业务A自己作用域下初始化一个,业务B自己作用域初始化一个。如果混用,那么业务A take方法得到的Futrue很有可能是业务B提交的任务。 - 第二
- 从案例的返回结果可以看出,如果在意返回结果的顺序跟提交的顺序一致,那么ExecutorCompletionService不适合该场景。请用传统的线程池提交任务。