Skip to content

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

注意事项

  1. CountDownLatch 不可重置:一旦计数到 0,不能再使用
  2. CyclicBarrier 可重置:所有线程通过后可重置再次使用
  3. Semaphore 控制的是许可数:不是线程数,但通常一对一
  4. Phaser 是最灵活的:综合了前两者的功能,但复杂度也最高
  5. Exchanger 只适合两线程:多于两个线程需要用其他方案

要点回顾

没有最好的工具,只有最合适的工具。记住每个工具的语义:

  • CountDownLatch:倒数门闩,一等多
  • CyclicBarrier:循环栅栏,多互等
  • Semaphore:信号量,控制并发
  • Phaser:阶段管理器,多阶段动态同步
  • Exchanger:交换器,两线程数据交换

基于 VitePress 构建