CyclicBarrier、CountDownLatch、Semaphore 各自的作用和用法区别

它们都是 java.util.concurrent 包下用于线程协作的强大工具,但各自的设计目的和使用场景截然不同。你可以把它们想象成交通管理工具:

  • CountDownLatch :像一个 发令枪。所有运动员(线程)在起跑线等待,枪响(计数器归零)后同时开始。
  • CyclicBarrier :像一个 集合点。一组远足的人(线程)必须全部到达某个地点后,才能一起继续前进。
  • Semaphore :像一个 票务系统。一个停车场只有有限的车位(许可),有车(线程)离开,才有新车可以进入。

下面我们进行详细的对比。


1. CountDownLatch(倒计时门闩)

核心作用让一个或多个线程等待另一组线程完成操作。它是一次性的,计数器不能被重置。

工作机制

  1. 初始化时设定一个计数值(比如 N)。
  2. 任何线程可以调用 countDown() 方法来减少计数。
  3. 其他线程调用 await() 方法会阻塞,直到计数值减到零,所有等待的线程才会被释放,继续执行。

典型用法

  • 主线程等待多个子线程初始化完成后再继续执行。
  • 模拟并发测试,确保所有线程都准备就绪后同时开始运行。
  • 等待多个远程服务调用结束后再进行结果聚合。

代码示例

// 主线程等待 5 个工作线程全部完成

java 复制代码
public class CountDownLatchDemo {
    public static void main(String[] args) throws InterruptedException {
        CountDownLatch latch = new CountDownLatch(5); // 初始化计数器为5

        for (int i = 0; i < 5; i++) {
            new Thread(() -> {
                System.out.println(Thread.currentThread().getName() + " 完成任务");
                latch.countDown(); // 计数器减1
            }).start();
        }

        latch.await(); // 主线程在此等待,直到计数器变为0
        System.out.println("所有线程已完成任务,主线程继续执行");
    }
}

2. CyclicBarrier(循环屏障)

核心作用让一组线程互相等待,直到所有线程都到达一个公共的屏障点,然后一起继续执行。它是可循环使用的。

工作机制

  1. 初始化时设定一个计数值(参与线程数 N)和一个可选的 Runnable 任务(屏障动作)。
  2. 每个线程执行到屏障点时调用 await() 方法,该方法会阻塞。
  3. 当第 N 个线程调用 await() 后,计数达到要求,所有被阻塞的线程会被同时唤醒继续执行 ,并且计数器自动重置到初始值,可以再次使用。
  4. 可选的 Runnable 屏障任务会在所有线程被唤醒前,由最后一个到达屏障的线程执行。

典型用法

  • 分步计算任务,将一个大任务分成N个小任务,每个线程处理一部分,最后在屏障点合并结果。
  • 多轮游戏或模拟,需要所有玩家都准备好才开始下一轮。

代码示例

// 3个线程一起到一个屏障点集合,然后继续

java 复制代码
public class CyclicBarrierDemo {
    public static void main(String[] args) {
        CyclicBarrier barrier = new CyclicBarrier(3, () -> {
            // 屏障动作,由最后到达的线程执行
            System.out.println("所有线程已就位,开始执行下一步任务");
        });

        for (int i = 0; i < 3; i++) {
            new Thread(() -> {
                try {
                    System.out.println(Thread.currentThread().getName() + " 到达屏障前");
                    barrier.await(); // 在此等待其他线程
                    System.out.println(Thread.currentThread().getName() + " 冲破屏障,继续执行");
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }).start();
        }
    }
}

3. Semaphore(信号量)

核心作用控制同时访问某个特定资源的线程数量,用于做流量控制或资源池管理。

工作机制

  1. 初始化时设定一个许可数量permits)。
  2. 线程通过 acquire() 方法获取一个许可(如果还有可用许可),许可数量减1。
  3. 如果许可已被拿完,后续调用 acquire() 的线程会被阻塞,直到有其他线程释放许可
  4. 线程使用完资源后,通过 release() 方法释放许可,许可数量加1,并唤醒一个等待的线程。

典型用法

  • 数据库连接池,限制同时获取连接的线程数。
  • 流量控制,如限制某个接口的并发调用数。
  • 控制访问特定设备的线程数(如打印机)。

代码示例

// 一个只有3个许可的信号量,控制同时执行的线程数

java 复制代码
public class SemaphoreDemo {
    public static void main(String[] args) {
        Semaphore semaphore = new Semaphore(3); // 初始化3个许可

        for (int i = 0; i < 10; i++) { // 启动10个线程
            new Thread(() -> {
                try {
                    semaphore.acquire(); // 获取许可
                    System.out.println(Thread.currentThread().getName() + " 获取许可,执行中...");
                    Thread.sleep(2000); // 模拟业务操作
                    System.out.println(Thread.currentThread().getName() + " 执行完毕,释放许可");
                } catch (InterruptedException e) {
                    e.printStackTrace();
                } finally {
                    semaphore.release(); // 释放许可
                }
            }).start();
        }
    }
}

总结对比表

特性 CountDownLatch CyclicBarrier Semaphore
核心目的 一个/多个线程等待其他线程完成 一组线程互相等待至一个公共点 控制同时访问资源的线程数
计数器 递减(countDown()),一次性 递增(await()),可循环重置 递减(acquire())/递增(release())
可重用性 不可重用(计数到0后失效) 可重用(自动重置计数器) 可重用(许可可重复获取和释放)
主要方法 await(), countDown() await() acquire(), release()
计数操作 任意线程减少计数 等待的线程自身增加计数 需要访问资源的线程获取和释放
典型场景 主等子、启动发令枪 多步骤任务同步、多轮协作 资源池、流量控制、互斥锁(Permits=1)
相关推荐
2501_916766548 小时前
【JVM】类的加载机制
java·jvm
Sag_ever8 小时前
Java数组详解
java
张np8 小时前
java基础-ConcurrentHashMap
java·开发语言
早日退休!!!8 小时前
进程与线程的上下文加载_保存及内存映射
开发语言
jllllyuz8 小时前
MATLAB实现蜻蜓优化算法
开发语言·算法·matlab
冰暮流星8 小时前
javascript逻辑运算符
开发语言·javascript·ecmascript
flysh059 小时前
如何利用 C# 内置的 Action 和 Func 委托
开发语言·c#
一嘴一个橘子9 小时前
spring-aop 的 基础使用 - 4 - 环绕通知 @Around
java
小毅&Nora9 小时前
【Java线程安全实战】⑨ CompletableFuture的高级用法:从基础到高阶,结合虚拟线程
java·线程安全·虚拟线程