Skip to content
Java 并发与锁机制 · 第 7 篇 / 共 9 篇
领域编程与框架
专题Java 专题
当前序列Java 并发与锁机制
阅读位置第 7 篇 / 共 9 篇当前专题第 3 个序列 / 共 10 个序列

CountDownLatch、CyclicBarrier、Semaphore 怎么选:JUC 线程协作工具实战理解

很多并发问题,不是“要不要互斥”,而是:

  • 某些线程得等别人做完
  • 一批线程要同时开始下一阶段
  • 同一时刻只能放有限数量的线程进入

这时真正有用的往往不是普通锁,而是线程协作工具。

先说结论

三者可以先这样记:

  • CountDownLatch:等一批任务做完
  • CyclicBarrier:一批线程彼此等待后再一起继续
  • Semaphore:控制同时允许多少线程进入

它们关注的不是“共享变量保护”,而是“线程之间的协作关系”。

一、CountDownLatch:一个等多个

最典型的场景是:

  • 主线程等多个子任务完成后再继续

例如:

  • 页面聚合多个远程调用
  • 启动阶段等待多个模块初始化

基本理解

初始化一个计数值,每完成一个任务就 countDown() 一次,等待方通过 await() 阻塞,直到计数归零。

特点

  • 计数只能减,不能重置
  • 更像“一次性门闩”

所以它很适合:

  • 一次性等待全部完成

二、CyclicBarrier:多个互相等

它更适合:

  • 一批线程都达到某个阶段后,再一起进入下一阶段

例如:

  • 并行计算分阶段推进
  • 压测时模拟多个线程同时起跑

基本理解

到达屏障点的线程先等待,直到达到指定数量,所有线程一起继续。

特点

  • 可以重复使用
  • 支持 barrier action

所以它比 CountDownLatch 更偏:

  • 多线程“会合”

三、Semaphore:控制并发进入数量

它适合的问题是:

  • 某个资源同一时刻只能允许有限数量线程访问

例如:

  • 连接池限流
  • 接口并发数限制
  • 有限资源抢占

基本理解

它维护一组许可:

  • acquire() 获取许可
  • release() 归还许可

特点

  • 不是互斥锁
  • 可以允许多个线程同时进入,只要不超过许可数

如果许可数是 1,它也能退化成近似互斥的效果,但通常不建议把它当普通锁替代品来用。

四、一个更实用的场景对照

场景 1:等 10 个任务全部完成

用:

  • CountDownLatch

场景 2:3 个线程每轮都要先都准备好,再进入下一轮

用:

  • CyclicBarrier

场景 3:某个资源最多允许 5 个线程同时访问

用:

  • Semaphore

五、和锁的区别到底在哪

锁更关注的是:

  • 某段临界区同一时刻能不能只有一个线程执行

而协作工具更关注的是:

  • 线程什么时候该等
  • 线程何时可以一起继续
  • 线程进入的数量要不要被控制

所以很多人把这三类工具和锁放在一起记,其实容易混。

六、几个常见坑

1. CountDownLatch 用完还想复用

它是一次性的,归零后不能重置。

2. CyclicBarrier 某个线程异常退出,其他线程一直等

这类问题在并行计算里很常见,所以实际使用时要考虑超时和异常处理。

3. Semaphore 获取了许可却忘记释放

这和锁忘记解锁一样,最终会把资源用死。

一句话总结

这三类工具本质上都在解决“线程怎么配合”:

  • 等完成,用 CountDownLatch
  • 等会合,用 CyclicBarrier
  • 限并发,用 Semaphore

先把问题类型分清,再选工具,会比记 API 更有用。

延伸阅读相关文章优先当前专题,再补跨专题关联。
同一序列 · 顺着当前主线继续读BlockingQueue 和生产者消费者模型适合沿着 JUC、volatile、CAS、AQS 和锁实现这条主线往下看。Java 专题 · Java 并发与锁机制同一序列 · 顺着当前主线继续读ThreadLocal 为什么容易出问题适合沿着 JUC、volatile、CAS、AQS 和锁实现这条主线往下看。Java 专题 · Java 并发与锁机制同专题其他序列 · 共享标签:选型对比线程池拒绝策略怎么选适合把线程池参数、拒绝策略、CompletableFuture 和 ForkJoin 放在一起连续看。Java 专题 · Java 线程池与异步编排同专题其他序列 · 共享标签:选型对比AtomicLong 和 LongAdder 怎么选适合把 synchronized、CAS、LongAdder 和并发容器的适用边界放到一起看。Java 专题 · Java 锁实现与并发细节跨专题关联 · 同场景:方案选型半同步复制和异步复制怎么选适合把 next-key lock、一致性读、复制延迟和分库分表边界放回同一条一致性主线上判断。MySQL 专题 · MySQL 事务、锁与高可用边界跨专题关联 · 同场景:方案选型分词器、Tokenizer 和 IK 到底怎么选适合把分词、复杂查询和深分页放到一起深入看。Elasticsearch 专题 · Elasticsearch 查询与分析细节
继续阅读Java 并发与锁机制当前序列第 7 篇 / 共 9 篇当前专题第 3 个序列 / 共 10 个序列
往前看
上一篇StampedLock 怎么理解回到当前序列上一章上一序列Java 集合与容器从第 1 篇开始:Java 集合怎么选
往后看
下一篇BlockingQueue 和生产者消费者模型继续当前序列下一章下一序列Java 线程池与异步编排从第 1 篇开始:线程池参数怎么配

把零散经验整理成可查、可复用、可持续更新的企业级知识门户