CountDownLatch、CyclicBarrier、Semaphore 有何区别?
结论先行:三者都是 JUC 同步工具,但语义完全不同:CountDownLatch 是倒数门闩,等 N 个线程各自报数(countDown)完成后放行等待者;CyclicBarrier 是循环栅栏,N 个线程互相等待到齐后再同时出发且可重置复用;Semaphore 是信号量,用许可数控制同时访问资源的线程上限,本质是限流。
三者对比
| 维度 | CountDownLatch | CyclicBarrier | Semaphore |
|---|---|---|---|
| 核心语义 | 计数归零即放行 | 凑齐人数再开跑 | 许可证控制并发数 |
| 等待关系 | 主线程等一组任务完成 | 线程们互相等齐 | 每个线程申请/释放许可 |
| 可否复用 | 否(一次性) | 是(reset 后循环使用) | 是(一直可用) |
| 典型场景 | 服务启动等所有模块就绪 | 分批并行计算后统一汇总 | 数据库连接池、接口限流 |
import java.util.concurrent.*;
public class SyncToolsDemo {
public static void main(String[] args) throws Exception {
// 1) CountDownLatch:3 个任务都 countDown 后主线程才继续
CountDownLatch latch = new CountDownLatch(3);
for (int i = 0; i < 3; i++) {
new Thread(() -> {
try { Thread.sleep(50); }
catch (InterruptedException e) { Thread.currentThread().interrupt(); }
finally { latch.countDown(); }
}).start();
}
latch.await();
// 2) CyclicBarrier:3 个线程到齐后一起“开跑”
CyclicBarrier barrier = new CyclicBarrier(3,
() -> System.out.println("全员到齐,开跑"));
for (int i = 0; i < 3; i++) {
new Thread(() -> {
try { barrier.await(); } catch (Exception e) { }
}).start();
}
// 3) Semaphore:最多发放 2 个许可
Semaphore sem = new Semaphore(2);
sem.acquire();
try { /* 访问受限资源 */ } finally { sem.release(); }
}
}
常见追问 / 记忆点
- 记忆点:Latch 在“倒计时”、Barrier 在“凑人头”、Semaphore 在“卡并发数”。
- 追问:CountDownLatch 计数不能回拨;CyclicBarrier 有人中断/超时后会破坏栅栏,可用 reset 重建。
- 追问:Semaphore 同样有公平/非公平模式,acquire 可响应中断,release 会唤醒等待线程。