深入解析Java并发工具:CountDownLatch与Semaphore核心区别与应用场景。掌握CountDownLatch的"一次性门闩"机制,实现多线程任务协同;了解Semaphore的"资源许可证"模式,有效控制并发访问量。本文通过王者荣耀开黑、充电桩管理等生动案例,详解两者在初始化协调、连接池管理、接口限流等场景的应用。面试必备:对比表格清晰呈现核心差异,常见追问深度剖析,助你轻松应对Java并发面试难题。附2025年最新Java面试宝典资源,提升技术实力,斩获高薪offer!
大家好,今天咱们来聊聊面试中高频出现的并发工具:CountDownLatch
和 Semaphore
。这两个家伙都属于java.util.concurrent
包里的“利器”,名字听着唬人,但理解了机制其实很清晰。面试官最爱问它们的区别和适用场景,搞明白了,能让你在并发题上轻松过关!
2025年Java面试宝典重磅分享:
链接: https://pan.baidu.com/s/1RUVf75gmDVsg8MQp4yRChg?pwd=9b3g 提取码: 9b3g
想象一个场景:你组织了一场王者荣耀开黑,需要等5个队友都确认“准备好了”,才能一起点“开始匹配”。CountDownLatch
干的就是这个协调的活儿!
new CountDownLatch(5)
)。每当一个任务完成,就调用countDown()
,计数器减1。调用await()
的线程(比如主线程)会阻塞,直到计数器减到0才放行。await()
会立刻放行,无法重置复用。想再用?new一个新的。await()
,等待一组工作线程完成任务(调用countDown()
)。countDownLatch.countDown()
作为发令枪)// 伪代码口述思路:
// 1. main线程创建CountDownLatch(5)
// 2. 启动5个工作线程,每个线程干完活就latch.countDown()
// 3. main线程调用latch.await(),等5个都完成
// 4. 5个都countDown后,main线程解除阻塞,继续执行汇总逻辑
再想象一个场景:小区里只有3个充电桩,却有10辆电动车要充电。Semaphore
就是管理这3个充电桩的“物业”。
new Semaphore(3)
)。线程想访问共享资源,必须先调用acquire()
申请一个许可证(如果没有空闲的,线程就阻塞等待)。用完后,必须调用release()
归还许可证,以便其他线程使用。release()
增加许可(虽然不常用),也可以通过构造方法或reducePermits
减少(需小心)。Semaphore
和线程池,它们都能限制并发数,区别在哪?”(线程池管理的是工作线程本身的生命周期和任务队列;Semaphore
只控制并发访问资源的入口数量,不管理线程。)// 伪代码口述思路(连接池):
// 1. 初始化Semaphore(10) // 假设10个连接
// 2. 线程想获取连接:先semaphore.acquire() // 申请许可证,没许可证就等
// 3. 拿到许可证后,从池里取一个真实连接用
// 4. 用完连接,放回池里
// 5. 调用semaphore.release() // 归还许可证,让其他线程能用
| 特性 | CountDownLatch | Semaphore |
| :----------- | :-------------------------- | :---------------------------- |
| 核心目的 | 等待事件完成 | 控制并发访问资源量 |
| 计数器 | 减到0触发 | 许可证数量 (>=0) |
| 重置性 | 不可重置 (一次性) | 可复用 (acquire/release) |
| 修改方向 | 只能减 (countDown
) | 可增 (release
) 可减 (acquire
) |
| 主要用法 | 主线程 await()
等子线程 | 工作线程自己 acquire/release
|
| 典型场景 | 初始化协调、多任务结果汇总 | 连接池、资源池、限流 |
简单记:
CountDownLatch
是“等大家干完活开会”;Semaphore
是“名额有限,先到先得,用完归还”。
CountDownLatch
能代替 join()
吗?为什么更好?
join()
更灵活,join()
必须等线程结束,CountDownLatch
只要任务完成就能countDown()
,线程不一定结束。而且可以等待多个不同的线程组。Semaphore
的 acquire()
和 release()
数量必须严格匹配吗?
acquire()
多了:许可证变负数,后续线程阻塞,可能死锁。release()
多了:许可证总数变多,可能超过你期望的限制,导致过度并发。切记在finally
块中释放!怎么用 Semaphore
实现互斥锁 (Mutex)?
Semaphore(1)
。acquire()
相当于lock()
,release()
相当于unlock()
。这就是一个非公平的互斥锁。CyclicBarrier
和 CountDownLatch
有啥区别?
CountDownLatch
:主等子,一次性,计数器单向减。CyclicBarrier
:子等子(所有线程互相等),可循环使用(reset()
),计数器内部重置,到达屏障点可以执行一个可选任务(Runnable)。被问到区别时,不要死背概念。结合具体例子:
“CountDownLatch 就像火箭发射前的检查,要等所有系统(引擎、导航、通信…)都回报‘Ready’(
countDown()
),总控台(await()
)才点火发射。是一次性的协作。Semaphore 更像停车场入口的闸机,总共50个车位(
permits=50
)。车来了要拿卡(acquire()
)才能进,没卡等;车走了要还卡(release()
)让别的车进。它控制的是同时停车的数量,闸机(Semaphore)可以一直用。”
用好并发工具,写出高效稳定的代码是面试加分项! 如果你正在准备面试,需要系统刷题和深入理解Java并发,可以考虑购买面试鸭会员获取海量真题解析和深度资料。对了,通过 面试鸭返利网 (mianshiyafanli.com) 找我购买,还能享受 25元返利 哦!
理解清楚 CountDownLatch
和 Semaphore
的设计思想和适用场景,下次遇到并发协调问题,你就能快速选出最合适的工具了!加油!
扫码联系我返利
(当前返利8元,金额随官方实际价格波动,最好提前咨询)
面试鸭小程序码
美团大额优惠券,给自己加个鸡腿吧!
今日有支付宝大红包赶快领,手慢无
支付宝扫码领取1-8元无门槛红包