文章中如果有图看不到,可以点这里去 csdn 看看。从那边导过来的,文章太多,没法一篇篇修改好。

Java 锁相关详解【七、Java 并发工具类全景图:Semaphore、CountDownLatch、CyclicBarrier 与 Phaser】

Java 并发工具类全景图:Semaphore、CountDownLatch、CyclicBarrier 与 Phaser

在前几篇文章中,我们已经深入解析了 Java 锁机制以及 AQS 的底层实现原理。
AQS 并不仅仅是 ReentrantLock 的基础,它还是一整套并发工具类的核心支撑。

高并发编程 中,除了锁以外,我们经常需要:

  • 控制线程访问资源的数量;
  • 等待一组线程执行完毕;
  • 让一批线程在同一时刻同步执行;
  • 管理分阶段的并发任务。

JUC 提供了四个重要的工具类来解决这些问题:

  • Semaphore
  • CountDownLatch
  • CyclicBarrier
  • Phaser

本文将对它们逐一解析,并比较适用场景。


一、Semaphore:信号量

1.1 概念

Semaphore(信号量)用于控制 同时访问某个资源的线程数量
本质上,Semaphore 维护了一个许可数 state(由 AQS 共享模式管理)。

  • acquire():请求一个许可,如果没有可用的许可,则阻塞。
  • release():释放一个许可,唤醒等待线程。

1.2 示例:限流

Semaphore semaphore = new Semaphore(3); // 最多允许 3 个线程同时访问

Runnable task = () -> {
    try {
        semaphore.acquire();
        System.out.println(Thread.currentThread().getName() + " 获得许可");
        Thread.sleep(1000);
    } catch (InterruptedException e) {
        Thread.currentThread().interrupt();
    } finally {
        semaphore.release();
    }
};

for (int i = 0; i < 10; i++) {
    new Thread(task).start();
}

输出结果显示,任意时刻最多只有 3 个线程执行。

1.3 应用场景

  • 数据库连接池限流
  • 接口请求限流
  • 控制并发访问资源

二、CountDownLatch:倒计时门闩

2.1 概念

CountDownLatch 用于 等待一组线程完成任务

  • 初始化时设置计数器值 N。
  • 每次调用 countDown() 计数器减 1。
  • 调用 await() 的线程会阻塞,直到计数器为 0。

2.2 示例:等待任务完成

CountDownLatch latch = new CountDownLatch(3);

for (int i = 0; i < 3; i++) {
    new Thread(() -> {
        try {
            Thread.sleep(1000);
            System.out.println(Thread.currentThread().getName() + " 执行完成");
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
        } finally {
            latch.countDown();
        }
    }).start();
}

latch.await();
System.out.println("所有任务完成,主线程继续执行");

2.3 应用场景

  • 并行任务汇总(等待所有子任务完成后再继续)。
  • 服务启动依赖(主线程等待多个模块加载完成)。

三、CyclicBarrier:循环屏障

3.1 概念

CyclicBarrier 用于 让一组线程在屏障点相互等待,直到所有线程都到达后才一起继续执行。

CountDownLatch 的区别:

  • CountDownLatch 是一次性的;
  • CyclicBarrier 可重用(循环屏障)。

3.2 示例:多人游戏加载

CyclicBarrier barrier = new CyclicBarrier(3, 
    () -> System.out.println("所有玩家已就绪,开始游戏!"));

for (int i = 0; i < 3; i++) {
    new Thread(() -> {
        try {
            System.out.println(Thread.currentThread().getName() + " 正在加载...");
            Thread.sleep((long) (Math.random() * 2000));
            System.out.println(Thread.currentThread().getName() + " 加载完成");
            barrier.await(); // 等待其他线程
        } catch (Exception e) {
            e.printStackTrace();
        }
    }).start();
}

3.3 应用场景

  • 多人协作任务(所有线程必须到齐才能执行下一步)。
  • 并行计算阶段同步(如 MapReduce 的阶段切换)。

四、Phaser:分阶段同步器

4.1 概念

Phaser 是 JDK 7 引入的 分阶段同步器,功能更强大,替代部分 CyclicBarrierCountDownLatch 的使用场景。

它允许:

  • 动态注册/注销参与方;
  • 多阶段的同步;
  • 更灵活的任务协调。

4.2 示例:多阶段任务

Phaser phaser = new Phaser(3); // 初始 3 个线程

for (int i = 0; i < 3; i++) {
    new Thread(() -> {
        System.out.println(Thread.currentThread().getName() + " 阶段1开始");
        phaser.arriveAndAwaitAdvance(); // 等待其他线程

        System.out.println(Thread.currentThread().getName() + " 阶段2开始");
        phaser.arriveAndAwaitAdvance(); // 等待其他线程
    }).start();
}

4.3 应用场景

  • 分阶段计算任务(如多轮迭代的科学计算)。
  • 动态线程参与(如并发下载,任务可随时加入或退出)。

五、工具类对比

工具类核心机制是否可重用典型应用场景
Semaphore信号量许可并发访问控制(限流、资源池)
CountDownLatch倒计时等待多个线程完成(一次性)
CyclicBarrier屏障同步阶段性任务同步(固定线程数)
Phaser分阶段屏障多阶段任务、动态参与者

六、工程实践建议

  1. 并发限流 → 用 Semaphore

    • 控制访问资源的最大并发数。
  2. 一次性等待多个线程完成 → 用 CountDownLatch

    • 典型场景:主线程等待多个子任务完成。
  3. 阶段性同步 → 用 CyclicBarrier

    • 多个线程需要同时进入下一阶段。
  4. 多阶段、动态参与 → 用 Phaser

    • 更复杂的分阶段任务调度。

七、总结

  • Semaphore:控制并发资源访问数,适合限流场景。
  • CountDownLatch:一次性等待任务完成,适合任务汇总。
  • CyclicBarrier:阶段性同步点,适合需要多线程对齐的计算。
  • Phaser:支持动态参与的分阶段同步器,适合复杂任务。

一句话总结:
掌握这四个工具类,可以让你在不同的并发控制场景下游刃有余,选择合适的工具胜过手写复杂的同步逻辑。

posted @ 2025-09-04 16:58  NeoLshu  阅读(4)  评论(0)    收藏  举报  来源