最常用且推荐的方式是使用ExecutorService.invokeAll()方法,它阻塞等待全部任务完成并返回按提交顺序排列的Future列表,再通过get()获取结果;也可用submit()逐个提交并灵活控制超时或取消,或用CompletionService按完成顺序获取结果。

Java 中批量提交 Callable 任务并获取所有结果,最常用且推荐的方式是使用 ExecutorService.invokeAll() 方法。它会阻塞等待全部任务完成,并统一返回包含所有 Future 的列表,再通过 get() 提取结果。
用 invokeAll() 一次性提交并等待全部完成
这是最简洁、语义最清晰的做法。适用于你明确需要等所有任务都执行完再统一处理结果的场景。
- 调用
executor.invokeAll(callables),传入Collection> - 方法返回
List,顺序与提交顺序一致> - 遍历每个
Future,调用get()获取结果(会阻塞直到该任务完成) - 注意:如果某个任务抛出异常,
get()会封装为ExecutionException抛出,需捕获处理
示例:
ExecutorService executor = Executors.newFixedThreadPool(4);
List<callable>> tasks = Arrays.asList(
() -> { Thread.sleep(100); return "Task-1"; },
() -> { Thread.sleep(200); return "Task-2"; },
() -> { throw new RuntimeException("Oops"); }
);
<p>try {
List<future>> futures = executor.invokeAll(tasks);
for (Future<string> future : futures) {
try {
System.out.println(future.get()); // 按提交顺序输出结果或异常
} catch (ExecutionException e) {
System.err.println("任务执行失败: " + e.getCause().getMessage());
}
}
} finally {
executor.shutdown();
}</string></future></p></callable>手动 submit() + 遍历 get():更灵活但需自行管理
当需要控制超时、提前取消、或对部分任务做差异化处理时,可逐个 submit(),再分别 get()。
- 适合需要设置单个任务超时(如
future.get(3, TimeUnit.SECONDS)) - 可对特定
Future调用cancel(true)中断未完成任务 - 注意:必须显式调用
shutdown()或shutdownNow()释放线程资源
用 CompletionService 包装:按完成顺序获取结果
如果你不关心提交顺序,只希望“谁先做完谁先处理”,就用 ExecutorCompletionService。
- 内部维护一个完成队列,调用
take()或poll()可以按实际完成时间获取结果 - 避免了因某个慢任务阻塞整个结果处理流程
- 特别适合异构耗时任务(比如有的查缓存、有的查数据库)
示例片段:
ExecutorService executor = Executors.newFixedThreadPool(4);
CompletionService<integer> completionService = new ExecutorCompletionService(executor);
<p>// 提交任务
for (int i = 0; i {
Thread.sleep((long) (Math.random() <em> 2000));
return i </em> i;
});
}</p>
<p>// 按完成顺序取结果
for (int i = 0; i </p></integer>注意事项和最佳实践
- 始终记得关闭
ExecutorService(shutdown()+awaitTermination()),防止线程泄漏 - 不要在
Callable中吞掉异常——否则get()无法感知失败 - 若任务量极大(如百万级),避免一次性构造巨大集合;可分批提交 + 分批
invokeAll() - 考虑使用
ForkJoinPool.commonPool()替代自建线程池(尤其在 Java 8+ 的并行流场景下)
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











