Java并发编程工具实践:CountDownLatch同步管理

作者:袖梨 2026-06-24
CountDownLatch是Java中一次性使用的同步工具,核心是通过初始化计数器N,调用await()阻塞等待、countDown()递减计数,计数归零后唤醒所有等待线程且不可重置,适用于“等全部做完”或“一起开跑”两类场景。

CountDownLatch 是 Java 并发编程中用于线程同步协调的轻量级工具,核心价值在于“等待多个操作完成后再统一推进”,而不是控制资源访问或重复复位。它用一次就归零,不可重置,这点和 CyclicBarrier 有本质区别。

CountDownLatch 的关键行为特征

理解它的行为,比记住 API 更重要:

  • 构造时指定一个正整数 N,作为初始计数 —— 这个 N 代表你期望等待的“完成事件”数量,不一定是线程数(比如一个线程可能调用多次 countDown)
  • await() 会让调用线程阻塞,直到计数变为 0;若此时计数已是 0,则立即返回,不阻塞
  • countDown() 是无条件递减操作,每次调用只减 1,成功与否不反馈,也不校验调用者身份
  • 一旦计数归零,所有正在 await() 的线程被唤醒,后续再调用 await() 也直接通过 —— 它不再具备“门闩”功能,只是个“已解锁”状态

典型使用模式:两种相反但高频的场景

CountDownLatch 的用法其实围绕两个方向展开,选错模式会导致逻辑混乱:

  • “等全部做完”模式:主线程调用 await(),多个工作线程各自完成任务后调用 countDown()。这是最常见用法,如服务启动时等待各模块初始化、多线程下载后合并结果
  • “一起开跑”模式:一个 CountDownLatch 初始化为 1,多个线程先 await() 阻塞,主线程(或裁判线程)在适当时机调用一次 countDown() —— 所有等待线程瞬间并发执行。常用于压力测试、秒杀模拟、批量任务统一开始

实际编码注意事项

看似简单,但容易踩坑:

立即学习“Java免费学习笔记(深入)”;

  • countDown() 必须确保被执行 —— 建议放在 finally 块里,防止因异常导致计数遗漏,造成 await() 永久阻塞
  • 避免在同一个 CountDownLatch 上混用两种模式,逻辑会难以维护
  • 不要依赖 getCount() 做业务判断(如“还剩2个没完成就发预警”),因为它是非原子读,且无法反映真实并发状态
  • 超时控制很重要:用 await(long, TimeUnit) 替代无参 await(),防止因某个子任务卡死而拖垮整个流程

它不适合做什么

明确边界,才能用得准确:

  • 不适用于需要反复重置计数的场景(该用 CyclicBarrier)
  • 不能替代 synchronized 或 ReentrantLock 做临界区保护
  • 不提供线程间数据传递能力,它只解决“时机同步”,不是“数据同步”
  • 不能限制并发数(那是 Semaphore 的职责)

相关文章

精彩推荐