在Java并发编程中,CountDownLatch、Semaphore和CyclicBarrier是三种常用的同步工具类,它们分别用于不同的线程协作场景。以下是它们的详细介绍:
一、CountDownLatch(倒计时门闩)
核心机制:
- 计数器同步:初始化时设置一个正整数计数器(如
n),代表需等待的事件数量。线程通过await()阻塞,直到计数器归零;其他线程通过countDown()将计数器减1。 - 一次性使用:计数器归零后无法重置,适合固定数量线程的同步。
- 底层实现:基于AQS(AbstractQueuedSynchronizer),通过共享锁模式实现线程阻塞与唤醒。
核心方法:
await():阻塞当前线程,直至计数器为0。countDown():减少计数器值,当计数器归零时唤醒所有等待线程。- 支持超时版本
await(long timeout, TimeUnit unit)避免无限等待。
典型场景:
- 并行任务同步:主线程等待多个子线程完成后再继续执行(如批量数据处理、多线程爬虫)。
- 多线程协作测试:确保所有线程同时开始执行,测试结果更准确。
示例代码:
CountDownLatch latch = new CountDownLatch(3);
// 工作线程
new Thread(() -> {
// 任务执行
latch.countDown(); // 任务完成后计数减1
}).start();
// 主线程等待
latch.await(); // 阻塞直到计数器归零
System.out.println("所有子任务已完成");
二、Semaphore(信号量)
核心机制:
- 许可证控制:初始化时设置许可证数量(如
permits),线程通过acquire()获取许可证(计数器减1),使用后通过release()释放(计数器加1)。 - 公平与非公平模式:公平模式按线程请求顺序分配许可证;非公平模式允许新线程优先获取。
- 动态调整:许可证数量可通过
release()动态增加。
核心方法:
acquire():获取一个许可证,无可用时阻塞。release():释放一个许可证,唤醒等待线程。tryAcquire():尝试获取许可证,不阻塞。
典型场景:
- 资源池管理:限制数据库连接池、线程池的并发访问数量。
- 限流控制:通过动态调整许可证数量控制API请求并发量。
- 共享资源互斥:多个线程访问共享文件时,通过信号量控制同时写入数量。
示例代码:
Semaphore semaphore = new Semaphore(3); // 3个许可证
// 工作线程
new Thread(() -> {
try {
semaphore.acquire(); // 获取许可证
// 访问共享资源
} finally {
semaphore.release(); // 释放许可证
}
}).start();
三、CyclicBarrier(循环屏障)
核心机制:
- 线程组同步:初始化时设置需等待的线程数量(如
parties),线程通过await()阻塞,直到所有线程到达屏障点;屏障触发后,所有线程继续执行。 - 可复用:屏障触发后计数器自动重置,可重复使用。
- 屏障动作:支持在屏障触发时执行一个
Runnable任务(如汇总结果)。
核心方法:
await():阻塞当前线程,直至所有线程到达屏障点。- 支持超时版本
await(long timeout, TimeUnit unit)避免无限等待。
典型场景:
- 多阶段任务协作:如并行算法中各子任务完成自身部分后,需在屏障处汇合并同步进行下一阶段计算。
- 资源初始化同步:多个线程依赖共享资源初始化,初始化完成后统一放行业务逻辑。
示例代码:
CyclicBarrier barrier = new CyclicBarrier(3, () -> {
System.out.println("所有线程已到达屏障点,继续执行后续操作");
});
// 工作线程
new Thread(() -> {
try {
// 执行任务
barrier.await(); // 等待其他线程
// 屏障触发后继续执行
} catch (InterruptedException | BrokenBarrierException e) {
e.printStackTrace();
}
}).start();
皿、三者对比
| 对比维度 | CountDownLatch | Semaphore | CyclicBarrier |
|---|---|---|---|
| 核心目的 | 等待特定数量线程完成 | 控制并发访问资源的线程数 | 让一组线程互相等待,达到同步点 |
| 计数器行为 | 仅递减,归零后不可重置 | 可递增/递减,支持动态调整 | 递减至零后自动重置,可重复使用 |
| 复用性 | 一次性使用 | 可重复使用(如资源池) | 可重复使用(如多阶段任务同步) |
| 典型场景 | 线程同步、任务聚合 | 限流、资源互斥、连接池管理 | 多线程协作、分阶段任务同步 |
| 底层机制 | 基于AQS共享锁 | 基于AQS许可证模型 | 基于AQS条件变量与锁 |
五、其他
1、Exchanger(交换器)
核心机制:
- 线程间数据交换:提供一个同步点,两个线程可以在此交换数据。
- 阻塞等待:如果一个线程先到达交换点,它会等待另一个线程到达后才能完成交换。
- 线程安全:内部实现已做好线程安全处理,无需额外加锁。
核心方法:
- exchange(V x):将当前线程的数据x与对方线程的数据进行交换,并返回对方线程的数据。
- exchange(V x, long timeout, TimeUnit unit):带有超时参数的交换数据方法,如果在指定时间内没有另一个线程到达交换点,则返回null或抛出异常。
典型场景:
- 生产者-消费者模型:生产者生产一批数据,消费者需要拿到这批数据来处理。使用Exchanger可以让生产者直接把数据交给消费者,而不需要通过队列等中间结构。
- 大数据分块处理:将数据分成两块,让两个线程分别处理。处理完一块后,两个线程可以用Exchanger交换数据,比如交换处理结果或者中间状态。
- 双人协作模拟:在模拟系统中,可能需要两个线程互相同步,比如模拟两个人在合作完成一项任务时需要交换信息的情况。
示例代码(生产者-消费者模型):
import java.util.concurrent.Exchanger;
public class ExchangerDemo {
public static void main(String[] args) {
Exchanger<String> exchanger = new Exchanger<>();
// 生产者线程
new Thread(() -> {
try {
String data = "生产的数据";
System.out.println("生产者生产数据: " + data);
String received = exchanger.exchange(data);
System.out.println("生产者收到消费者数据: " + received);
} catch (InterruptedException e) {
e.printStackTrace();
}
}).start();
// 消费者线程
new Thread(() -> {
try {
String data = exchanger.exchange(null); // 初始时消费者没有数据可交换
System.out.println("消费者收到生产者数据: " + data);
String response = "处理后的数据";
exchanger.exchange(response); // 发送处理后的数据给生产者
} catch (InterruptedException e) {
e.printStackTrace();
}
}).start();
}
}
2、Phaser
Phaser 是 Java 并发包(java.util.concurrent)中的一个高级同步工具类,专为协调多个线程分阶段执行任务而设计。 它结合了 CyclicBarrier 和 CountDownLatch 的功能,并提供了动态调整参与者数量、多阶段同步等高级特性,适用于复杂的并发任务流程控制。
核心机制
-
多阶段同步:
- Phaser 将任务划分为多个阶段(Phase),每个阶段结束时所有线程需同步,之后才能进入下一阶段。
- 阶段号从 0 开始递增,直至终止。
-
动态线程管理:
- 线程可通过
register()动态注册为参与者,或通过arriveAndDeregister()注销,无需在初始化时固定线程数量。 - 内部使用
AtomicLong存储状态,包含当前阶段号(高 32 位)、已注册参与者数(中 16 位)、未到达参与者数(低 16 位)。
- 线程可通过
-
状态推进:
arriveAndAwaitAdvance():线程完成任务后等待其他参与者到达,全部到达后自动推进阶段。- 重写
onAdvance(int phase, int parties)可自定义阶段结束行为(如数据汇总),返回true时 Phaser 终止。
核心特性
-
灵活性:
- 支持动态增减参与者,适用于线程数量不确定的场景。
- 支持多阶段同步,每个阶段可以有不同数量的参与者。
-
可重用性:
- 阶段自动重置,无需手动重置(如 CyclicBarrier)。
-
分层结构:
- 支持父子 Phaser 树,用于分布式任务协调。
-
自定义行为:
- 通过
onAdvance()方法在每个阶段结束时执行自定义逻辑,如日志记录、资源释放等。
- 通过
使用方法与代码示例
- 基础用法:三阶段任务同步:
import java.util.concurrent.Phaser;
public class PhaserDemo {
public static void main(String[] args) {
Phaser phaser = new Phaser(3); // 初始注册3个参与者
for (int i = 0; i < 3; i++) {
new Thread(() -> {
System.out.println(Thread.currentThread().getName() + " 完成阶段1");
phaser.arriveAndAwaitAdvance(); // 同步点1
System.out.println(Thread.currentThread().getName() + " 完成阶段2");
phaser.arriveAndAwaitAdvance(); // 同步点2
System.out.println(Thread.currentThread().getName() + " 完成阶段3");
phaser.arriveAndDeregister(); // 注销并推进
}).start();
}
}
}
- 动态任务注册:批量数据处理:
import java.util.List;
import java.util.concurrent.Phaser;
class BatchProcessor implements Runnable {
private final Phaser phaser;
private final List<String> data;
BatchProcessor(Phaser phaser, List<String> data) {
this.phaser = phaser;
this.data = data;
phaser.register(); // 动态注册新参与者
}
@Override
public void run() {
while (!data.isEmpty()) {
processBatch(data.subList(0, Math.min(10, data.size())));
phaser.arriveAndAwaitAdvance(); // 等待其他批次完成
}
phaser.arriveAndDeregister(); // 处理完毕,注销
}
private void processBatch(List<String> batch) {
// 数据处理逻辑
}
}
- 自定义阶段回调:
import java.util.concurrent.Phaser;
public class CustomPhaserDemo {
public static void main(String[] args) {
Phaser phaser = new Phaser(3) {
@Override
protected boolean onAdvance(int phase, int parties) {
System.out.println("阶段 " + phase + " 完成,参与线程数: " + parties);
return phase >= 2; // 执行3个阶段后终止
}
};
// 创建并启动线程(代码略,与基础用法示例类似)
}
}
优缺点分析
| 维度 | 优点 | 缺点 |
|---|---|---|
| 灵活性 | 支持动态增减参与者、多阶段同步 | 学习曲线陡峭,API 复杂于 CyclicBarrier |
| 可扩展性 | 适合大规模并发任务(如千人游戏房间) | 性能开销:状态维护和 CAS 操作在高并发下可能成为瓶颈 |
| 功能强大 | 支持分层、自定义终止条件、超时控制 | 不适用简单场景,单阶段或固定线程数时,CountDownLatch 更简单 |
应用场景
- 分治算法:如归并排序,每阶段需子任务全部完成。
- 爬虫系统:线程根据任务量动态加入/退出。
- 多玩家回合制游戏:每回合需所有玩家操作完毕。
- ETL 任务:分批次执行,批次间需同步。

811

被折叠的 条评论
为什么被折叠?



