如何让ExecutorService等待所有Future执行完成?附示例代码
如何让ExecutorService等待所有Future执行完成
嘿,我来帮你搞定这个需求!先看你的代码,其实已经用到了关键的invokeAll方法,它本身就实现了等待所有任务完成的核心逻辑,不过咱们可以再优化下细节,让代码更健壮。
核心原理:利用invokeAll的阻塞特性
ExecutorService.invokeAll()方法会一次性提交所有Callable任务,然后阻塞当前线程,直到所有任务都执行完毕(不管是正常结束还是抛出异常),之后才会返回包含所有Future的列表。这一步已经帮你完成了“等待所有Future执行完成”的核心需求。
优化后的完整代码
咱们来调整下你的代码,完善结果处理和线程池关闭的逻辑:
import java.util.ArrayList; import java.util.List; import java.util.concurrent.*; public class TaskWaitDemo { public static void main(String[] args) { try { // 创建固定大小的线程池 ExecutorService executor = Executors.newFixedThreadPool(5); List<Callable<String>> taskList = new ArrayList<>(5); // 构建任务列表(给每个任务加标识方便调试) for (int i = 1; i <= 5; i++) { final int taskId = i; taskList.add(new SimpleTask(taskId)); } // 关键:invokeAll会阻塞直到所有任务完成 List<Future<String>> futures = executor.invokeAll(taskList); // 关闭线程池:不再接受新任务,已提交的任务继续执行 executor.shutdown(); // 等待线程池所有任务执行完毕,设置超时时间(可根据业务调整) if (!executor.awaitTermination(10, TimeUnit.SECONDS)) { // 超时后强制关闭线程池,中断未完成的任务 executor.shutdownNow(); System.err.println("线程池超时强制关闭"); } // 遍历所有Future,获取结果并处理可能的异常 for (Future<String> future : futures) { try { String result = future.get(); System.out.println(result); } catch (ExecutionException e) { // 任务执行时抛出的异常会被包装在这里 System.err.println("任务执行失败:" + e.getCause().getMessage()); } } System.out.println("所有任务执行完成!Exit"); } catch (InterruptedException e) { // 处理等待过程中线程被中断的情况 Thread.currentThread().interrupt(); e.printStackTrace(); } } // 自定义的Callable任务 static class SimpleTask implements Callable<String> { private final int taskId; public SimpleTask(int taskId) { this.taskId = taskId; } @Override public String call() throws Exception { // 模拟任务耗时操作 Thread.sleep(1000 * taskId); return "任务" + taskId + "执行完成"; } } }
关键细节说明
invokeAll的作用:不用自己写循环判断每个Future是否完成,这个方法帮你一次性搞定等待所有任务结束。shutdown()+awaitTermination():shutdown()只是禁止线程池接受新任务,已经提交的任务会继续跑;awaitTermination()会阻塞等待,确保所有任务都执行完,避免程序提前退出。如果超时还没完成,就用shutdownNow()强制中断。- 异常处理:每个任务执行时的异常会被包装在
ExecutionException里,遍历Future的时候要逐个处理,避免漏掉某个任务的错误。
另一种写法:逐个提交任务并等待
如果你不想用invokeAll,也可以逐个提交任务,然后循环调用future.get()(这个方法会阻塞直到当前任务完成):
// 替换invokeAll部分的写法 List<Future<String>> futures = new ArrayList<>(); for (int i = 1; i <= 5; i++) { final int taskId = i; futures.add(executor.submit(new SimpleTask(taskId))); } // 逐个等待任务完成并获取结果 for (Future<String> future : futures) { try { System.out.println(future.get()); } catch (ExecutionException e) { System.err.println("任务执行失败:" + e.getCause().getMessage()); } }
这种写法和invokeAll的效果是一样的,只是invokeAll更简洁。
总结一下:你的原始代码已经走对了方向,只要补充好线程池关闭的逻辑和异常处理,就能完美实现“等待所有Future执行完成”的需求啦!
内容的提问来源于stack exchange,提问作者mcacorner
相关产品推荐
相关产品推荐

