ExecutorCompletionService 按任务实际完成先后顺序返回结果,其核心是用BlockingQueue暂存已完成Future,通过done回调自动入队,take()/poll()按完成顺序出队获取Future再调用get()得结果。

ExecutorCompletionService 是 Java 并发包中一个非常实用的工具,它能让你**按任务实际完成的先后顺序**获取结果,而不是按提交顺序或线程池执行顺序。它本质上是对 Executor 的增强包装,内部结合了 BlockingQueue(默认是 LinkedBlockingQueue)来暂存已完成的 Future,从而实现“谁先做完谁先被取到”。
核心原理:完成即入队,取即出队
当你调用 submit() 提交一个 Callable 任务时,ExecutorCompletionService 会:
- 将任务交给底层
Executor执行; - 同时创建一个封装了该任务的
Future,并注册一个内部监听机制(通过FutureTask的 done 状态回调); - 一旦任务完成(无论成功或异常),其
Future会被自动放入内部阻塞队列; - 调用
take()或poll()就能按完成顺序取出这些Future,再调用get()获取真实结果。
基本使用步骤(带示例)
假设你有 3 个耗时不同(模拟网络/IO 差异)的异步任务,希望按它们实际完成的顺序处理结果:
- 创建线程池(如
Executors.newFixedThreadPool(3)); - 用该线程池构造
ExecutorCompletionService<String>; - 循环提交
Callable<String>任务(例如返回耗时信息); - 调用
completionService.take()—— 它会**阻塞直到有任务完成**,然后返回最先完成的那个Future; - 对返回的
Future调用get()获取结果(此时不会阻塞,因为已确定完成)。
处理异常与超时的注意事项
任务可能抛出异常,或你不想无限等待:
立即学习“Java免费学习笔记(深入)”;
-
Future.get()仍可能抛出ExecutionException(包装原始异常)或InterruptedException,需捕获处理; - 若想非阻塞获取,可用
poll()(立即返回 null)或poll(long, TimeUnit)(带超时); - 注意:
take()和poll()返回的是Future,不是结果本身;必须调用get()才能拿到值或感知异常; - 如果某个任务失败但你不关心,可在
get()时 catchExecutionException后跳过,不影响后续已完成任务的获取。
对比 Future 列表 + 循环 isDone() 的劣势
不用 CompletionService 时,常见做法是保存所有 Future 到列表,再轮询 isDone() —— 这样效率低、不实时、易写成 busy-wait。而 CompletionService 基于阻塞队列,天然支持“完成即通知”,无空转开销,线程安全,语义清晰。


















