countdownlatch 是一次性同步工具,用于等待多个操作完成;初始计数器值即待等待事件总数,await() 阻塞直至归零或超时,countdown() 须在每个任务 finally 块中调用以确保执行。

CountDownLatch 是 Java 并发编程中轻量、可靠且一次性的同步辅助工具,适用于“等待多个操作全部完成后再继续”的典型场景。它不依赖线程归属,不关心谁调用 countDown(),只关注计数器是否归零——逻辑清晰,上手门槛低,但用错容易导致主线程无限等待或提前唤醒。
核心用法与关键约定
使用前需明确三点:计数器初始值(即待等待的事件总数)、哪些线程调用 await()(通常是协调者线程,如主线程)、哪些线程调用 countDown()(即执行任务的工作者线程)。计数器一旦归零,无法重置;若需循环复用,请改用 CyclicBarrier。
- 构造时传入正整数,如
new CountDownLatch(3)表示需等待 3 个信号 -
await()会阻塞当前线程,直到计数为 0 或被中断;建议搭配超时版本await(long, TimeUnit)防止死等 -
countDown()是无副作用的原子操作,可在任意线程中安全调用多次,但通常每个任务只调一次 - 务必确保每个任务路径(包括异常分支)都执行
countDown(),推荐放在finally块中
常见实用场景与写法
它不是万能锁,而是“完成门控”——适合表达“N件事做完,我才开始下一件事”。以下是最常落地的三类用法:
- 多线程初始化同步:服务启动时,多个模块(DB连接池、缓存预热、配置加载)并行初始化,主线程用 CountDownLatch 等待全部就绪再开放接口
-
并行任务结果汇总:将大计算拆成若干子任务(如分段统计日志),各子线程算完调
countDown(),主线程await()后统一 merge 结果 - 协同式资源就绪通知:例如下载场景中,多个线程分别下载文件分片,全部完成后主线程触发合并;或测试中模拟“所有依赖服务已启动”,才开始跑用例
避坑要点与健壮写法
看似简单,但生产环境出问题多源于边界疏忽。重点关注以下细节:
- 不要在持有锁或事务上下文时调用
await(),避免阻塞传播影响其他业务线程 - 避免在
await()前调用countDown()—— 计数器可能提前归零,导致 await 瞬间返回,失去等待意义 - 使用线程池提交任务时,
countDown()必须在任务体内部调用,不能靠 submit 返回的 Future 判断完成(Future.get() 是阻塞的,违背并行初衷) - 调试时可用
getCount()查看剩余计数,但仅限开发/监控,不可用于逻辑判断(竞态风险)
一个简洁可靠的模板
以下结构兼顾可读性与健壮性,适用于大多数任务型协作场景:
(伪代码示意,实际请按项目规范封装)- 创建
CountDownLatch latch = new CountDownLatch(taskList.size()) - 对每个 task,提交到线程池:
executor.submit(() -> { try { task.run(); } finally { latch.countDown(); } }) - 主线程执行
if (!latch.await(30, TimeUnit.SECONDS)) { throw new TimeoutException("tasks not finished"); } - 后续逻辑(汇总、上报、切换状态等)
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











