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)
相关推荐
程序猿追2 小时前
kotlin-retry 接入鸿蒙后,怎样确认它真的会停止重试
开发语言·kotlin·harmonyos
我是人✓2 小时前
Java文件上传三方式对比:从传统到SpringBoot
java·开发语言
智能直播3 小时前
网络RTMP拉流不卡顿、声音不同步,一文讲透缓冲区、时间戳
开发语言·ffmpeg·php·音视频·视频编解码
欣栀♡3 小时前
JVM 频繁 Full GC 先看日志括号里的 cause,jstat 的 FGC 在 JDK 8 上会骗人
java·jvm
IT研究室3 小时前
最新计算机毕业设计选题推荐-基于spring boot的葡萄酒文化宣传网站-网站-文档指导-Java-springboot
java·spring boot·课程设计
慧都小项3 小时前
MyEclipse 中创建和测试 JAX-RS REST 接口:从项目配置到 REST Explorer
java·restful·myeclipse·jax-rs
Wang's Blog3 小时前
Java 项目实战: 交友项目 - 入门案例:编写 Dubbo 服务提供者
java·dubbo·交友
墨云安全3 小时前
内网资产无法暴露到公网,怎么做安全检测?2026 年内网渗透测试的落地路径
开发语言·安全·网络安全·渗透测试·php
秋饼4 小时前
Spring AI 2.0 结构化输出企业级实战:从 BeanOutputConverter 源码到生产级解析容错
java·ai·技术分享·后端开发
摇滚侠4 小时前
《计算机是如何工作的:人人都能懂的计算机软硬件工作原理》第 9 章 机器码与汇编语言 阅读笔记 9
java·开发语言·笔记