JUC 同步工具对比
CountDownLatch、CyclicBarrier、Semaphore、Phaser、Exchanger……
名字都认识,但什么时候用哪个?
快速选择表
| 场景 | 工具 |
|---|---|
| 一个线程等待多个线程完成 | CountDownLatch |
| 多个线程互相等待 | CyclicBarrier / Phaser |
| 控制并发数量 | Semaphore |
| 多阶段动态同步 | Phaser |
| 两线程交换数据 | Exchanger |
工具对比
| 工具 | 等待方向 | 可重复 | 动态参与者 |
|---|---|---|---|
| CountDownLatch | 一等多 | 否 | 否 |
| CyclicBarrier | 多互等 | 是 | 否 |
| Semaphore | 控制并发数 | 是 | 是 |
| Phaser | 多阶段 | 是 | 是 |
| Exchanger | 数据交换 | 否(每次交换后重新开始) | 否 |
典型场景对比
场景一:服务启动
需求:主线程等待所有组件启动完成。
java
// CountDownLatch:完美匹配
CountDownLatch latch = new CountDownLatch(3);
startComponent("DB", latch);
startComponent("Cache", latch);
startComponent("MQ", latch);
latch.await(); // 主线程等待CyclicBarrier / Phaser 也能实现,但语义不如 CountDownLatch 清晰。
场景二:游戏开始
需求:所有玩家准备就绪后才能开始。
java
// CyclicBarrier:完美匹配
CyclicBarrier barrier = new CyclicBarrier(3);
player1.joinBarrier(barrier);
player2.joinBarrier(barrier);
player3.joinBarrier(barrier);
// 所有玩家都调用 await() 后,同时开始CountDownLatch 不太合适,因为玩家之间是互相等待关系。
场景三:限流
需求:同时最多 10 个请求访问数据库。
java
// Semaphore:完美匹配
Semaphore semaphore = new Semaphore(10);
for (Request req : requests) {
semaphore.acquire();
executeQuery(req);
semaphore.release();
}其他工具都不适合。
场景四:多阶段测试
需求:并行执行测试,每个测试需要经历 Setup → Execute → Teardown 三个阶段。
java
// Phaser:完美匹配
Phaser phaser = new Phaser(3);
test1.setup(phaser);
test2.setup(phaser);
test3.setup(phaser);
phaser.arriveAndAwaitAdvance(); // 所有测试完成 Setup
// ...CyclicBarrier 需要多次创建,Phaser 一个实例搞定。
场景五:双缓存交换
需求:写入线程和读取线程交换缓冲区。
java
// Exchanger:完美匹配
Exchanger<Buffer> exchanger = new Exchanger<>();
writer.exchange(exchanger); // 交换缓冲区
reader.exchange(exchanger); // 交换缓冲区其他工具都不合适。
综合对比代码
java
public class JUCToolsCompareDemo {
public static void main(String[] args) throws Exception {
System.out.println("=== JUC 同步工具对比 ===\n");
// 场景1:CountDownLatch - 等待多个任务完成
System.out.println("1. CountDownLatch - 主线程等待子任务完成");
countDownLatchDemo();
// 场景2:CyclicBarrier - 多线程互相等待
System.out.println("\n2. CyclicBarrier - 多线程分阶段执行");
cyclicBarrierDemo();
// 场景3:Semaphore - 限流控制
System.out.println("\n3. Semaphore - 限流控制");
semaphoreDemo();
// 场景4:Phaser - 多阶段动态同步
System.out.println("\n4. Phaser - 多阶段动态同步");
phaserDemo();
// 场景5:Exchanger - 线程间数据交换
System.out.println("\n5. Exchanger - 线程间数据交换");
exchangerDemo();
}
private static void countDownLatchDemo() throws InterruptedException {
int taskCount = 3;
CountDownLatch latch = new CountDownLatch(taskCount);
for (int i = 0; i < taskCount; i++) {
final int taskId = i;
new Thread(() -> {
try {
System.out.println(" 任务 " + taskId + " 执行完成");
} finally {
latch.countDown();
}
}).start();
}
System.out.println(" 主线程等待中...");
latch.await();
System.out.println(" 所有任务完成,主线程继续");
}
private static void cyclicBarrierDemo() throws InterruptedException {
int threadCount = 3;
CyclicBarrier barrier = new CyclicBarrier(threadCount, () -> {
System.out.println(" 所有线程到达屏障");
});
for (int i = 0; i < threadCount; i++) {
final int threadId = i;
new Thread(() -> {
try {
System.out.println(" 线程 " + threadId + " 到达屏障");
barrier.await();
System.out.println(" 线程 " + threadId + " 继续执行");
} catch (Exception e) {}
}).start();
}
Thread.sleep(1000);
}
private static void semaphoreDemo() throws InterruptedException {
Semaphore semaphore = new Semaphore(2);
for (int i = 0; i < 3; i++) {
final int threadId = i;
new Thread(() -> {
try {
semaphore.acquire();
System.out.println(" 线程 " + threadId + " 获取许可");
Thread.sleep(500);
semaphore.release();
} catch (InterruptedException e) {}
}).start();
}
Thread.sleep(2000);
}
private static void phaserDemo() throws InterruptedException {
Phaser phaser = new Phaser(3);
for (int i = 0; i < 3; i++) {
final int threadId = i;
new Thread(() -> {
System.out.println(" 线程 " + threadId + " 到达第一阶段");
phaser.arriveAndAwaitAdvance();
System.out.println(" 线程 " + threadId + " 进入第二阶段");
phaser.arriveAndDeregister();
}).start();
}
Thread.sleep(1000);
}
private static void exchangerDemo() throws InterruptedException {
Exchanger<String> exchanger = new Exchanger<>();
new Thread(() -> {
try {
System.out.println(" 线程A 交换数据");
String data = exchanger.exchange("A的数据");
System.out.println(" 线程A 收到: " + data);
} catch (InterruptedException e) {}
}, "ThreadA").start();
new Thread(() -> {
try {
Thread.sleep(100);
System.out.println(" 线程B 交换数据");
String data = exchanger.exchange("B的数据");
System.out.println(" 线程B 收到: " + data);
} catch (InterruptedException e) {}
}, "ThreadB").start();
Thread.sleep(1000);
}
}选择口诀
一等多 → CountDownLatch
多互等 → CyclicBarrier / Phaser
限流量 → Semaphore
多阶段 → Phaser
交换数据 → Exchanger注意事项
- CountDownLatch 不可重置:一旦计数到 0,不能再使用
- CyclicBarrier 可重置:所有线程通过后可重置再次使用
- Semaphore 控制的是许可数:不是线程数,但通常一对一
- Phaser 是最灵活的:综合了前两者的功能,但复杂度也最高
- Exchanger 只适合两线程:多于两个线程需要用其他方案
要点回顾
没有最好的工具,只有最合适的工具。记住每个工具的语义:
- CountDownLatch:倒数门闩,一等多
- CyclicBarrier:循环栅栏,多互等
- Semaphore:信号量,控制并发
- Phaser:阶段管理器,多阶段动态同步
- Exchanger:交换器,两线程数据交换
