[面试题] CountDownLatch、CyclicBarrier、Semaphore 有什么区别?分别适合什么场景?
📖 版块 A:知识讲解版 (温故知新)
-
CountDownLatch:
- 本质是一个倒计时门闩。
- 一个或多个线程等待,直到其他线程把计数减到 0。
- 一次性使用,计数归零后不能重置。
-
CyclicBarrier:
- 本质是一个可循环使用的屏障。
- 一组线程彼此等待,直到所有参与者都到达屏障点后再一起继续。
- 支持重复使用,支持 barrier action。
-
Semaphore:
- 本质是一个许可证控制器。
- 通过
acquire/release控制同时访问某资源的线程数量。 - 适合做限流、资源池并发控制。
-
底层共性:
- 都属于 JUC 同步器。
CountDownLatch和Semaphore底层都基于 AQS。CyclicBarrier内部则更多依赖ReentrantLock+Condition协调等待线程。
-
典型场景:
CountDownLatch:主线程等待多个子任务完成。CyclicBarrier:多线程分阶段并行计算。Semaphore:控制数据库连接数、接口并发数、线程池外层限流。
🎤 版块 B:面试实战版 (高手逐字稿)
Q:CountDownLatch、CyclicBarrier、Semaphore 有什么区别?分别适合什么场景?
💬 高手逐字稿:
这三个类都属于 JUC 里的同步工具,但它们解决的不是同一种问题。
CountDownLatch 解决的是“等完成”的问题。它内部有一个倒计时,主线程或者某个协调线程会在那儿等着,其他工作线程每完成一部分任务就 countDown() 一次,直到计数减到 0,等待线程才会继续执行。所以它特别像项目管理里的“收尾检查”,比如主线程要等 10 个分片任务都跑完,再做结果汇总。它的一个关键特征是一次性,计数器归零之后不能复原。
CyclicBarrier 解决的是“等人齐”的问题。它要求一组线程都跑到某个屏障点,谁先到谁先等,直到最后一个线程也到了,大家再一起往后走。这个模型很适合分阶段并行计算,比如多线程分别算一部分数据,每轮算完后统一汇总,再进入下一轮。它叫 Cyclic,就是因为它可以重复使用,不像 Latch 用一次就结束了。
Semaphore 解决的是“等名额”的问题。它内部维护一组许可证,线程拿到许可证才能继续执行,执行完再归还。它本质上不是让线程互相等待,而是在控制某个资源同时最多允许多少访问者。比如数据库连接池只有 20 个名额,或者某个下游接口我们只想放 50 个并发请求,Semaphore 就非常合适。
如果我用一句话区分它们:
CountDownLatch是“等任务做完”;CyclicBarrier是“等队友到齐”;Semaphore是“等资源名额”。
再补一个细节:很多人会把 CountDownLatch 和 CyclicBarrier 混掉。最大的区别其实是等待关系不同。CountDownLatch 通常是少数线程等多数线程,像主线程等子线程;CyclicBarrier 则是参与者彼此互等,大家地位是对等的。