countdownlatch 是一次性同步工具,内部计数器归零后唤醒所有等待线程且不可重置;需在任务完成(含异常)后用 try-finally 调用 countdown(),推荐使用带超时的 await() 防止永久阻塞。

CountDownLatch 是 Java 并发包中轻量、易用的同步工具,适合“一个线程等待多个线程完成任务后再继续执行”的典型场景。它不涉及锁竞争,性能好,语义清晰,但用错容易导致死锁或提前唤醒。
理解 CountDownLatch 的核心机制
CountDownLatch 内部维护一个计数器,初始化时指定正整数 N。每次调用 countDown() 计数器减 1;调用 await() 的线程会阻塞,直到计数器归零或被中断。计数器归零后,所有等待线程被唤醒,且后续再调用 await() 将直接返回——CountDownLatch 是一次性使用的,不可重置。
正确创建和启动任务集合
常见错误是把任务提交和倒计时逻辑混在一起,或漏掉 countDown()。推荐在任务执行完毕(无论成功或异常)后统一递减:
- 使用
try-finally确保即使抛异常也执行countDown() - 避免在主线程中提前调用
countDown(),否则可能造成await()立即返回 - 若任务由线程池执行,确保每个任务都对应一次
countDown(),不要在 submit 前就递减
合理设置超时,避免无限等待
await() 支持带超时参数的重载方法,强烈建议使用:
- 防止因某个子任务卡死、未调用
countDown()而导致主线程永久挂起 - 超时后可记录日志、清理资源或走降级逻辑,例如:
if (!latch.await(10, TimeUnit.SECONDS)) { throw new TimeoutException("Tasks not completed in time"); } - 注意:超时返回
false不代表任务已终止,只是等待失败,需自行判断是否需要中断任务线程(通常不建议粗暴 interrupt,应配合取消标志)
替代方案对比:什么情况下不该用 CountDownLatch
虽然简单高效,但它不适用于所有等待场景:
- 需要重复使用的等待逻辑 → 改用
CyclicBarrier - 等待的对象是异步计算结果(如 HTTP 请求、数据库查询)→ 更适合
CompletableFuture.allOf()或ExecutorService.invokeAll() - 需响应中断并主动退出等待 →
await()本身支持中断,但要注意捕获InterruptedException并恢复中断状态 - 任务之间有依赖关系或需传递结果 → CountDownLatch 不提供数据通道,应结合
ConcurrentHashMap、BlockingQueue或Future使用
不复杂但容易忽略细节。用对了,几行代码就能让并发控制变得干净可靠。











