CountDownLatch是主线程等待多个异步任务完成的轻量方案,需共享同一实例、countDown()置于finally块、await()必须设超时和异常处理。

CountDownLatch 初始化等待的典型用法
主线程启动多个异步初始化任务后,必须等全部完成才能继续,CountDownLatch 是最直接、轻量的方案。它不涉及线程池管理或回调嵌套,适合纯“等结果”场景。
核心逻辑:构造时传入任务总数,每个子任务执行完调用 countDown(),主线程调用 await() 阻塞直到计数归零。
为什么不能在子线程里反复 new CountDownLatch
常见错误是每个子任务都创建自己的 CountDownLatch 实例,导致主线程的 await() 永远不会返回——因为没人给它倒数。
- 所有子任务必须共享同一个
CountDownLatch实例 - 主线程创建它,并把引用传给每个子任务(比如通过 Runnable 构造参数或闭包捕获)
- 子线程只能调用
countDown(),绝不能调用await()
await() 超时和中断处理必须显式考虑
await() 默认无限阻塞,一旦某个子任务抛异常未调用 countDown(),主线程就卡死。生产代码必须加超时和异常兜底。
推荐写法:
if (!latch.await(10, TimeUnit.SECONDS)) {
throw new RuntimeException("Initialization timed out after 10s");
}
- 永远不要只写
latch.await() - 超时时间需结合最长单个任务耗时 + 并发度估算,别设成 1 秒就报错
- 如果子任务可能被中断(如用了
Future.cancel(true)),主线程也应响应InterruptedException
和 CompletableFuture.allOf() 的关键区别在哪
Java 8+ 可用 CompletableFuture.allOf() 替代,但它更适合需要汇总结果或链式编排的场景;而 CountDownLatch 更纯粹——只关心“是否全部结束”,不关心返回值或异常细节。
- 如果你只需“等完就往下走”,
CountDownLatch启动快、无对象开销、逻辑一目了然 - 如果你要收集各任务的返回值、统一处理异常、或后续接异步操作,
CompletableFuture更合适 - 注意:
allOf()不会传播子任务异常,得手动遍历get()才能发现失败项
真正容易被忽略的是子任务异常吞没问题:哪怕一个线程抛了 RuntimeException,只要没在 finally 里调用 countDown(),计数就卡住。务必把 countDown() 放进 finally 块里。










