CompletionService
是 Java 并发包(java.util.concurrent
)里的一个 "异步结果收割机" 。
它把「提交任务」和「拿结果」两件事解耦:
你只管往里扔任务;它帮你盯着谁先跑完,让你 按完成顺序 取结果,而不是按提交顺序。
一、核心 API(就 4 个常用方法)
方法 | 作用 |
---|---|
submit(Callable/Runnable) |
扔一个任务进去 |
take() |
阻塞 直到有任务完成,返回完成的 Future |
poll() |
立刻返回,如果没有已完成的任务返回 null |
poll(timeout, unit) |
等指定时间,超时返回 null |
二、使用套路(3 步曲)
-
创建线程池 + CompletionServic
ExecutorService pool = Executors.newFixedThreadPool(4); CompletionService<String> cs = new ExecutorCompletionService<>(pool);
-
批量提
for (int i = 0; i < 10; i++) { final int no = i; cs.submit(() -> { Thread.sleep(ThreadLocalRandom.current().nextInt(1000, 3000)); return "task-" + no; }); }
-
按完成顺序收割
for (int i = 0; i < 10; i++) { String result = cs.take().get(); // 谁先跑完谁先拿到 System.out.println(result); } pool.shutdown();
三、场景举例
-
并发下载:10 张图片同时开线程下,下好一张立刻显示一张,不用等全部下完。
-
并发调用多服务:A、B、C 三个微服务都查一遍,谁返回快就用谁的数据。
-
并行计算:跑 100 个 Monte-Carlo 任务,每算完一个就汇总,不用等全部结束再统计。
四、对比"裸用 FutureList"
方式 | 拿结果顺序 | 是否阻塞 |
---|---|---|
List<Future> |
按提交顺序 | 会卡在还没跑完的任务 |
CompletionService |
按完成顺序 | 只阻塞在 take() ,总有结果就返回 |
五、完整可运行 Demo
import java.util.concurrent.*;
public class Demo {
public static void main(String[] args) throws Exception {
ExecutorService pool = Executors.newFixedThreadPool(4);
CompletionService<Integer> cs = new ExecutorCompletionService<>(pool);
// 1. 提交 5 个耗时不一样的任务
for (int i = 1; i <= 5; i++) {
final int sec = i;
cs.submit(() -> {
TimeUnit.SECONDS.sleep(sec);
return sec;
});
}
// 2. 按完成顺序打印
for (int i = 0; i < 5; i++) {
Integer who = cs.take().get();
System.out.println("任务 " + who + " 秒完成!");
}
pool.shutdown();
}
}
运行结果(每次都一样):
任务 1 秒完成!
任务 2 秒完成!
任务 3 秒完成!
任务 4 秒完成!
任务 5 秒完成!
一句话总结:
CompletionService = 线程池 + 优先队列 ,让你 先做完的先拿走,写并行流水线最舒服。