日期: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,超时后取消未开始任务并让任务响应中断。
- 主线程等待不是忙轮询,应使用阻塞/异步组合。
面试官追问
- 如何收集部分成功结果?
- CompletionService 有什么优势?
- 如何设置整体超时而非每个任务各等一次?
面试官追问参考答案
1. 如何收集部分成功结果?
为每个 Future 使用 handle/exceptionally 转成包含成功或异常的 Result 包装,allOf 后统一汇总;业务定义最小成功数或关键任务。不能让一个异常导致已成功结果丢失而无记录。
2. CompletionService 有什么优势?
任务完成后 Future 进入完成队列,调用方按实际完成顺序 take,快任务无需等待先提交的慢任务。适合边完成边处理、首个成功和降低结果滞留时间。
3. 如何设置整体超时而非每个任务各等一次?
记录绝对 Deadline,使用 allOf().orTimeout 或带总超时的等待;超时后取消剩余任务。若逐个 get,每次超时都使用“剩余时间”,不能每个任务重新给完整超时。
学习清单
- 能比较 CompletableFuture、CompletionService 和 CountDownLatch。
- 理解异常、超时、取消和部分成功。