Java中的CountDownLatch、CyclicBarrier和Semaphore详解
字数 1539 2025-11-06 12:41:12

Java中的CountDownLatch、CyclicBarrier和Semaphore详解

1. 基本概念与作用

CountDownLatchCyclicBarrierSemaphore是JUC包中常用的同步工具类,用于协调多线程之间的执行顺序或资源分配:

  • CountDownLatch:允许一个或多个线程等待其他线程完成操作后再执行。
  • CyclicBarrier:让一组线程互相等待,达到屏障点后同时继续执行,可重复使用。
  • Semaphore:控制同时访问特定资源的线程数量,通过许可证(permits)实现限流。

2. CountDownLatch 详解

核心机制

  • 初始化时指定一个计数器(如new CountDownLatch(3))。
  • 线程调用countDown()时计数器减1,调用await()的线程会阻塞直到计数器归零。

使用场景

  • 主线程等待多个子线程完成任务后再继续(如并行计算后汇总结果)。
  • 服务启动时等待所有依赖组件初始化完成。

示例代码

CountDownLatch latch = new CountDownLatch(2);

// 子线程完成任务后递减计数器
new Thread(() -> {
    System.out.println("任务1完成");
    latch.countDown();
}).start();

new Thread(() -> {
    System.out.println("任务2完成");
    latch.countDown();
}).start();

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

特点:计数器不可重置,一次性使用。


3. CyclicBarrier 详解

核心机制

  • 初始化时指定线程数量(如new CyclicBarrier(3))和一个可选的屏障动作(Runnable)。
  • 线程调用await()时阻塞,直到指定数量的线程都到达屏障点,然后所有线程同时继续执行。

使用场景

  • 多阶段任务需要同步(如并行计算中每阶段需等待所有线程就绪)。
  • 模拟并发测试(多个线程同时触发操作)。

示例代码

CyclicBarrier barrier = new CyclicBarrier(3, () -> {
    System.out.println("所有线程到达屏障,执行屏障动作");
});

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

特点:计数器可重置(通过reset()方法),支持重复使用。


4. Semaphore 详解

核心机制

  • 初始化时指定许可证数量(如new Semaphore(3))。
  • 线程通过acquire()获取许可证(若无许可证则阻塞),通过release()释放许可证。

使用场景

  • 限制资源并发访问数(如数据库连接池、接口限流)。
  • 控制线程池中任务提交速率。

示例代码

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

for (int i = 0; i < 5; i++) {
    new Thread(() -> {
        try {
            semaphore.acquire(); // 获取许可证
            System.out.println(Thread.currentThread().getName() + "占用资源");
            Thread.sleep(2000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            semaphore.release(); // 释放许可证
            System.out.println(Thread.currentThread().getName() + "释放资源");
        }
    }).start();
}

特点:支持公平/非公平模式,许可证可动态增减。


5. 三者的对比

特性 CountDownLatch CyclicBarrier Semaphore
计数器是否可重置 否(一次性) 是(可重复使用) 是(许可证可回收)
核心作用 等待其他线程完成 线程间相互等待 控制资源访问数量
调用方法 await()/countDown() await() acquire()/release()
适用场景 主从协作 多阶段同步 资源池、限流

6. 注意事项

  1. CountDownLatchawait()可设置超时时间,避免无限等待。
  2. CyclicBarrier的屏障动作由最后到达屏障的线程执行。
  3. Semaphorerelease()方法可在任意线程调用,即使未先调用acquire()(可能导致许可证数量超过初始值)。

通过合理选择这三种工具,可以高效解决线程同步和资源控制问题。

Java中的CountDownLatch、CyclicBarrier和Semaphore详解 1. 基本概念与作用 CountDownLatch 、 CyclicBarrier 和 Semaphore 是JUC包中常用的同步工具类,用于协调多线程之间的执行顺序或资源分配: CountDownLatch :允许一个或多个线程等待其他线程完成操作后再执行。 CyclicBarrier :让一组线程互相等待,达到屏障点后同时继续执行,可重复使用。 Semaphore :控制同时访问特定资源的线程数量,通过许可证(permits)实现限流。 2. CountDownLatch 详解 核心机制 初始化时指定一个计数器(如 new CountDownLatch(3) )。 线程调用 countDown() 时计数器减1,调用 await() 的线程会阻塞直到计数器归零。 使用场景 主线程等待多个子线程完成任务后再继续(如并行计算后汇总结果)。 服务启动时等待所有依赖组件初始化完成。 示例代码 特点 :计数器不可重置,一次性使用。 3. CyclicBarrier 详解 核心机制 初始化时指定线程数量(如 new CyclicBarrier(3) )和一个可选的屏障动作(Runnable)。 线程调用 await() 时阻塞,直到指定数量的线程都到达屏障点,然后所有线程同时继续执行。 使用场景 多阶段任务需要同步(如并行计算中每阶段需等待所有线程就绪)。 模拟并发测试(多个线程同时触发操作)。 示例代码 特点 :计数器可重置(通过 reset() 方法),支持重复使用。 4. Semaphore 详解 核心机制 初始化时指定许可证数量(如 new Semaphore(3) )。 线程通过 acquire() 获取许可证(若无许可证则阻塞),通过 release() 释放许可证。 使用场景 限制资源并发访问数(如数据库连接池、接口限流)。 控制线程池中任务提交速率。 示例代码 特点 :支持公平/非公平模式,许可证可动态增减。 5. 三者的对比 | 特性 | CountDownLatch | CyclicBarrier | Semaphore | |--------------------|----------------------|-----------------------|----------------------| | 计数器是否可重置 | 否(一次性) | 是(可重复使用) | 是(许可证可回收) | | 核心作用 | 等待其他线程完成 | 线程间相互等待 | 控制资源访问数量 | | 调用方法 | await()/countDown() | await() | acquire()/release() | | 适用场景 | 主从协作 | 多阶段同步 | 资源池、限流 | 6. 注意事项 CountDownLatch 的 await() 可设置超时时间,避免无限等待。 CyclicBarrier 的屏障动作由最后到达屏障的线程执行。 Semaphore 的 release() 方法可在任意线程调用,即使未先调用 acquire() (可能导致许可证数量超过初始值)。 通过合理选择这三种工具,可以高效解决线程同步和资源控制问题。