Yihui’s Blog

主线程如何得知线程池多个并行任务完成并收集结果?

日期:2026-07-12
标签:#面试 #八股 #后端 #Java并发 #线程池 #场景题

一句话答案

可用 CompletableFuture.allOf 等待全部任务并汇总结果;需要按完成顺序消费用 CompletionService,只等待完成信号可用 CountDownLatch。

面试口语版

如果每个任务有返回值,我优先用 CompletableFuture:每个任务 supplyAsync 提交到指定有界线程池,allOf 组合完成,之后逐个 join 收集。要处理超时、异常、取消和线程池隔离,不能用公共池执行阻塞任务。若希望哪个先完成就先处理,用 ExecutorCompletionService,它内部完成队列避免按提交顺序等待慢任务。CountDownLatch 适合只等待 N 个任务结束,结果需另存线程安全容器;invokeAll 简单但会阻塞到全部完成。

代码示例

List<CompletableFuture<Result>> futures = tasks.stream()
    .map(task -> CompletableFuture.supplyAsync(task::call, executor))
    .toList();

CompletableFuture.allOf(futures.toArray(CompletableFuture[]::new)).join();
List<Result> results = futures.stream()
    .map(CompletableFuture::join)
    .toList();

关键细节

  • allOf 本身不返回结果列表,需要从原 Future 收集。
  • 任一失败是全失败还是保留部分结果要由业务定义。
  • 设置总 Deadline,超时后取消未开始任务并让任务响应中断。
  • 主线程等待不是忙轮询,应使用阻塞/异步组合。

面试官追问

  1. 如何收集部分成功结果?
  2. CompletionService 有什么优势?
  3. 如何设置整体超时而非每个任务各等一次?

面试官追问参考答案

1. 如何收集部分成功结果?

为每个 Future 使用 handle/exceptionally 转成包含成功或异常的 Result 包装,allOf 后统一汇总;业务定义最小成功数或关键任务。不能让一个异常导致已成功结果丢失而无记录。

2. CompletionService 有什么优势?

任务完成后 Future 进入完成队列,调用方按实际完成顺序 take,快任务无需等待先提交的慢任务。适合边完成边处理、首个成功和降低结果滞留时间。

3. 如何设置整体超时而非每个任务各等一次?

记录绝对 Deadline,使用 allOf().orTimeout 或带总超时的等待;超时后取消剩余任务。若逐个 get,每次超时都使用“剩余时间”,不能每个任务重新给完整超时。

学习清单

  • 能比较 CompletableFuture、CompletionService 和 CountDownLatch。
  • 理解异常、超时、取消和部分成功。
Maintained by · YihuiEdit on GitHub

Keep reading

View all posts