如何在不等待全部任务完成时获取Future的执行结果?
这个问题我太熟了——你遇到的是Future顺序调用get()导致的阻塞问题:明明Call2这类快任务早就执行完了,却因为你按提交顺序遍历Future列表,前面的慢任务(Call1)没完成,后面的结果就没法输出。别担心,Java并发包里专门有个工具能完美解决这个需求:ExecutorCompletionService。
问题根源
你原来的代码里,fut.get()是阻塞方法:当你按顺序遍历Future列表时,第一个Future如果对应Call1(慢任务),程序会卡在这个get()上,直到Call1执行完毕,哪怕后面的5个Call2早就做完了,也得等着。这就完全浪费了并行执行的优势。
解决方案:用ExecutorCompletionService跟踪已完成任务
ExecutorCompletionService的核心作用就是帮你监听所有提交的任务,一旦有任务完成,就把它的Future放到一个队列里。你只需要从这个队列里取结果,就能拿到最早完成的任务的结果,不用再按提交顺序等待。
修改后的完整代码如下:
public class ParallelAdder { public static void main(String[] args) throws ExecutionException, InterruptedException { ExecutorService executor = Executors.newFixedThreadPool(10); // 用ExecutorCompletionService包装你的线程池 ExecutorCompletionService<Integer> completionService = new ExecutorCompletionService<>(executor); // 提交任务,和原来逻辑一致,只是用completionService.submit for (int i = 0; i < 10; i++) { if (i % 2 == 0) { completionService.submit(new Call1()); } else { completionService.submit(new Call2()); } } // 循环获取所有已完成的任务结果 for (int i = 0; i < 10; i++) { // take()会阻塞,直到有任务完成,返回对应的Future Future<Integer> completedFuture = completionService.take(); // 这里的get()不会阻塞,因为任务已经完成了 System.out.println("From future is " + completedFuture.get()); } // 别忘了关闭线程池,释放资源 executor.shutdown(); } } class Call1 implements Callable<Integer> { public Integer call() throws Exception { System.out.println("Calling 1"); try { Thread.sleep(1000); // 把原代码的100000改成1000,方便快速测试 } catch (InterruptedException e) { e.printStackTrace(); } return 10; } } class Call2 implements Callable<Integer> { public Integer call() throws Exception { System.out.println("Calling 2"); try { Thread.sleep(10); } catch (InterruptedException e) { e.printStackTrace(); } return 20; } }
代码解释
- 包装线程池:用
ExecutorCompletionService包裹你原来的FixedThreadPool,它会自动接管任务完成的监听逻辑。 - 提交任务:和原来的提交方式几乎一样,只是换成
completionService.submit()。 - 获取结果:调用
completionService.take(),这个方法会阻塞直到有任务完成,返回该任务的Future。此时调用get()不会再阻塞,因为任务已经执行完毕。 - 关闭线程池:最后记得调用
executor.shutdown(),避免线程池一直占用资源。
效果验证
运行这段代码,你会看到所有的Calling 2先打印出来,紧接着就输出对应的From future is 20;而Calling 1打印后,等待1秒左右才输出From future is 10。完全实现了“任务一完成就输出结果”的需求,不用再等慢任务拖后腿。
额外小技巧
如果你不想阻塞等待(比如需要同时做其他事情),可以用completionService.poll()方法,它会立刻返回:如果有已完成的任务就返回对应的Future,没有就返回null。不过对于你的需求,take()是更合适的选择,因为你需要处理所有10个任务的结果。
内容的提问来源于stack exchange,提问作者js_248

