适用:Java 并发、AQS、线程同步面试;问题由浅入深,附带【标准答案】+【面试官连环追问】
基础概念类
Q1:简单说下三个工具各自作用、使用场景?
参考答案
CountDownLatch:闭锁。一个 / 多个线程等待其他 N 个线程执行完成,一次性使用,不可重置。 场景:多任务并行查询、服务启动等待初始化、压测闸门。CyclicBarrier:循环栅栏。一组线程互相等待,所有人到达屏障点再继续;支持循环复用,可设置屏障回调任务。 场景:多阶段分段并行计算。Semaphore:信号量。控制同时访问资源的线程数量,实现限流、资源抢占。 场景:接口限流、连接池、有限资源调度。
面试官追问 追问 1:CountDownLatch 能不能实现循环等待?为什么? 追问 2:CyclicBarrier 的回调任务由哪个线程执行?
Q2:CountDownLatch 和 CyclicBarrier 最核心区别?
参考答案
- 等待关系不同
- CountDownLatch:外部等待线程等待工作线程;等待方与执行方可以完全分离。
- CyclicBarrier:线程之间互相等待,参与等待的是同一组线程。
- 复用性
- CountDownLatch:state 归 0 后永久失效,不能重置。
- CyclicBarrier:一轮结束自动重置,可循环使用。
- 回调
- CountDownLatch:无内置回调。
- CyclicBarrier:支持 barrierAction,凑齐线程后执行。
- 异常传播
- CyclicBarrier:一个线程中断 / 超时 → 屏障破损,其余等待线程抛出
BrokenBarrierException; - CountDownLatch 单个线程异常不会连锁影响其他等待线程。
面试官追问 追问 1:如果我想要重复等待一组任务完成,选谁? 追问 2:能不能用 CountDownLatch 模拟实现 CyclicBarrier?有什么弊端?
底层原理类(面试重点)
Q3:三者底层基于什么实现?
参考答案
- CountDownLatch:AQS 共享模式,state 充当计数器。
- Semaphore:AQS 共享模式;支持公平 / 非公平两种模式。
- CyclicBarrier:没有直接使用 AQS ,底层
ReentrantLock + Condition,外加Generation(代机制)实现循环与屏障破损标记。
面试官追问 追问 1:AQS 共享锁与独占锁区别? 追问 2:Semaphore 公平和非公平区别,什么时候选公平?
Q4:CountDownLatch await ()、countDown () 底层流程?
参考答案
- await ():调用
tryAcquireShared,state>0 则进入 AQS 队列阻塞;state=0 才放行。 - countDown():
releaseShared,CAS 自旋 state-1;当 state 减到 0 时唤醒所有等待线程。
面试官追问 追问:getCount () 获取的值一定准确吗?为什么不能用于业务判断?
Q5:CyclicBarrier Generation 代的作用,什么叫屏障破损?
参考答案 Generation 用来区分每一轮等待。 破损触发条件:
- 等待线程调用 await 被中断;
- await 超时抛出 TimeoutException; 屏障一旦破损,其他阻塞线程立刻收到
BrokenBarrierException。调用 reset () 可以手动恢复屏障。
面试官追问 追问:屏障破损后不 reset 继续使用会发生什么?
使用踩坑 & 生产实践(高频手写题场景)
Q6:使用 CountDownLatch 有哪些坑?
参考答案
- countDown () 不要写在 try 内部,务必放在 finally;任务异常会导致计数不减,永久阻塞。
- 生产环境禁止裸 await (),优先使用带超时
await(time,unit)防止死锁。 - 初始化 count 值要和 countDown 调用次数匹配。
- 不可循环复用,循环场景不要复用同一个实例。
面试官追问 追问:线程池配合 CountDownLatch 代码怎么写?
Q7:CyclicBarrier 典型坑?
参考答案
- 实际参与 await 的线程数 ≠ parties → 永久阻塞。
- barrierAction 由最后到达的线程执行,不要放超长耗时逻辑。
- 出现线程中断 / 超时导致屏障破损,所有等待线程抛异常。
- reset () 时机不对,正在等待的线程会收到异常。
面试官追问 追问:如何优雅捕获 BrokenBarrierException?
Q8:Semaphore 有哪些使用注意点?
参考答案
- acquire () 获取许可,release () 释放;release 不要随意调用,避免许可膨胀。
- 最好保证 acquire 与 release 成对,建议 finally 释放。
- 支持 tryAcquire 非阻塞获取,避免无限阻塞。
- 公平信号量上下文切换开销更大,并发高场景默认非公平。
面试官追问 追问:可以一个线程多次 acquire、多次 release 吗?会出现什么问题?
场景选型题(面试官最喜欢现场辨析)
Q9:场景选择题,分别选用什么?
场景 1:调用 3 个微服务并行查询,全部返回再组装结果 → CountDownLatch 场景 2:3 组线程分阶段计算,阶段全部完成再进入下一阶段 → CyclicBarrier 场景 3:限制最多同时 20 个线程访问文件资源 → Semaphore
面试官追问 追问:如果使用 CyclicBarrier 实现并行调用多个接口行不行?有什么缺点?
灵魂深度追问(大厂常考)
Q10:Semaphore 和线程池核心参数 maximumPoolSize 限流区别?
参考答案
- ThreadPoolExecutor:限制工作线程数量;任务队列还能堆积任务。
- Semaphore:限制并发执行业务的数量,和线程池大小无关,可以用于跨线程池、跨应用资源限流。
Q11:CountDownLatch 与 Join 对比?
参考答案 thread.join () 只能等待指定线程;CountDownLatch 更加灵活: 可以等待任意一批线程、支持超时、线程池场景友好;join 无法设置超时(JDK 无原生超时 API)。
精简对比速查表(面试可以直接默写)
表格
| 特性 | CountDownLatch | CyclicBarrier | Semaphore |
|---|---|---|---|
| 核心作用 | 等待一批任务执行完毕 | 线程互相等待,统一推进 | 控制并发访问数量 |
| 复用 | 一次性,不可重置 | 自动循环复用 | 许可可反复获取释放 |
| 底层 | AQS 共享锁 | ReentrantLock + Condition | AQS 共享锁 |
| 回调 | 无 | 支持屏障回调 | 无 |
| 计数器 | 单向递减 | 每轮自动重置 | 可增可减(许可) |
| 典型异常 | InterruptedException | BrokenBarrierException、TimeoutException | InterruptedException |
附赠面试一句话背诵模板
- CountDownLatch:一个线程等 N 个线程,一次性门闩
- CyclicBarrier:N 个线程互相等,集合完毕再出发,支持多轮
- Semaphore:通行证,控制同一时刻多少线程能进入资源
