countdownlatch 的核心作用是让一个或多个线程等待其他线程完成指定数量的操作后再继续执行,其计数器归零后不可重置,需通过初始化时传入的任务总数(非线程数)控制同步,子任务须确保每次执行 countdown() 且置于 finally 块中,等待方应使用带超时的 await() 并正确处理中断。

CountDownLatch 的核心作用是让一个或多个线程等待,直到其他线程完成一组操作后再继续执行。它不控制线程的执行顺序,而是通过计数器做“门栓”——计数归零前,等待线程阻塞;归零后,所有等待线程被唤醒,且之后再调用 await 不会阻塞(因为计数已为 0)。
初始化时明确任务总数
创建 CountDownLatch 时传入的 int 参数代表“需要完成的任务数量”,不是线程数,也不是步骤数,而是要 countDown() 调用的总次数。比如启动 5 个异步子任务,就 new CountDownLatch(5);若每个子任务内部需多次通知(如分阶段上报),应改用 CyclicBarrier 或自定义逻辑,避免误调用 countDown() 导致计数提前归零。
- 常见错误:把线程数当成任务数,结果某线程执行多次 countDown(),导致主线程过早唤醒
- 推荐做法:在发起子任务前确定好总依赖数,用 final 变量或常量声明,增强可读性
子任务完成时务必调用 countDown()
每个子任务结束(无论成功或失败)都应确保 countDown() 被执行一次。遗漏会导致主线程永久阻塞;重复调用虽安全(计数不会变负),但掩盖了逻辑错误。
- 建议包裹在 finally 块中,例如:
try { /* 执行任务 */ } finally { latch.countDown(); } - 若使用 CompletableFuture 或 ExecutorService 提交任务,可在 thenRun、whenComplete 等回调里调用 countDown()
等待方调用 await() 并合理处理超时与中断
主线程或协调线程调用 await() 进入等待。生产环境强烈建议使用带超时的版本(await(long, TimeUnit)),防止因子任务异常未触发 countDown() 而无限挂起。
- 超时返回 false,此时可记录日志、清理资源、抛出异常或降级处理
- 若 await() 被中断(Thread.interrupt()),会抛出 InterruptedException,需捕获并恢复中断状态(
Thread.currentThread().interrupt();) - 不要忽略返回值或异常,否则可能掩盖系统不可用问题
注意 CountDownLatch 的一次性特性
计数归零后,latch 不可重置。如果需要反复同步(如周期性批处理),应新建实例,或改用 CyclicBarrier、Phaser。
- 不能通过反射或其它方式重置内部计数器,这是设计使然
- 若业务场景天然是一次性协作(如服务启动时加载多个配置模块),CountDownLatch 正合适
- 频繁创建新实例无性能压力,其内部实现轻量,无需池化复用











