
本文讲解如何在多线程任务中,通过 `countdownlatch` 同步等待多个 `callable` 任务完成,同时安全获取其返回值;重点说明实现逻辑的合理性、潜在风险及更优实践。
CountDownLatch 本身不负责传递结果,它仅用于线程间的“计数同步”——即阻塞调用线程直到所有预设任务完成。而 Callable 的返回值则需通过 Future 显式获取。你提供的代码在同步语义上是正确的:两个 Person 任务提交后,主线程调用 countDownLatch.await() 确保两者均已执行完毕(无论成功或异常),再通过 future1.get() 和 future2.get() 安全读取结果。这种“先等完成、再取结果”的模式符合并发编程最佳实践。
以下是优化后的完整示例(含异常处理与资源管理):
public class Person implements Callable{ private final CountDownLatch latch; private final int value; public Person(CountDownLatch latch, int value) { this.latch = latch; this.value = value; } @Override public Integer call() throws Exception { try { // 模拟实际耗时/可能失败的业务逻辑(如网络请求、计算) Thread.sleep(100); return value * 2; // 示例:返回加工后的值 } finally { latch.countDown(); // 确保无论成功/异常都释放计数器 } } }
public class Main {
public static void main(String[] args) {
CountDownLatch latch = new CountDownLatch(2);
ExecutorService executor = Executors.newFixedThreadPool(2);
Future f1 = executor.submit(new Person(latch, 5));
Future f2 = executor.submit(new Person(latch, 4));
try {
latch.await(); // 阻塞至两个任务均调用 countDown()
// 此时可安全获取结果(get() 不会阻塞,因 await 已保证完成)
int result1 = f1.get(); // 若任务抛异常,此处抛 ExecutionException
int result2 = f2.get();
System.out.println("Sum: " + (result1 + result2)); // 输出:Sum: 18
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
System.err.println("Main thread interrupted");
} catch (ExecutionException e) {
System.err.println("Task execution failed: " + e.getCause());
} finally {
executor.shutdown(); // 推荐使用 shutdown() + awaitTermination()
try {
if (!executor.awaitTermination(5, TimeUnit.SECONDS)) {
executor.shutdownNow();
}
} catch (InterruptedException e) {
executor.shutdownNow();
Thread.currentThread().interrupt();
}
}
}
} ⚠️ 关键注意事项:
- CountDownLatch.await() 不捕获任务异常:即使某个 Callable 抛出异常,countDown() 仍会在 finally 中执行,await() 仍会返回。因此必须对每个 Future.get() 单独处理 ExecutionException。
- 不要依赖 CountDownLatch 判断结果有效性——它只表示“已执行完毕”,而非“执行成功”。
- 若需统一收集多个 Future 结果并处理失败情况,可考虑 CompletableFuture.allOf() 或自定义聚合逻辑。
- ExecutorService 务必显式关闭,避免线程泄漏;推荐使用 try-with-resources(JDK 19+)或 shutdown() + awaitTermination() 组合。
✅ 总结:你的原始设计思路合理,CountDownLatch 与 Callable/Future 的组合是解决“等待 N 个异步任务完成后再汇总结果”问题的经典方案。只需补充异常处理、资源清理和语义明确性,即可投入生产使用。









