一、CountDownLatch
CountDownLatch可以理解为一个倒计时计数器,它允许一个或多个线程等待,直到其他线程完成操作。
通俗解释:就像运动会的百米赛跑,所有运动员(线程)在起跑线等待,裁判(主线程)鸣枪(countDown)后,所有运动员才能开始跑。
核心方法:
-
CountDownLatch(int count)
:构造函数,count为计数器的初始值 -
await()
:使当前线程等待,直到计数器减到0 -
countDown()
:计数器减1
示例代码:
public class CountDownLatchDemo {
public static void main(String[] args) throws InterruptedException {
// 模拟5个玩家加载游戏场景
int playerCount = 5;
CountDownLatch latch = new CountDownLatch(playerCount);
for (int i = 1; i <= playerCount; i++) {
new Thread(() -> {
try {
// 模拟加载时间
Thread.sleep((long) (Math.random() * 3000));
System.out.println(Thread.currentThread().getName() + " 加载完成");
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
latch.countDown(); // 计数器减1
}
}, "玩家-" + i).start();
}
System.out.println("等待所有玩家加载完成...");
latch.await(); // 主线程等待
System.out.println("所有玩家加载完成,游戏开始!");
}
}
实际应用场景
-
多任务开始前的统一初始化
-
并行计算,等待所有计算完成
-
服务启动时等待所有组件加载完成
二、CyclicBarrier
CyclicBarrier让一组线程到达一个屏障时被阻塞,直到最后一个线程到达屏障时,所有线程才会继续执行。
通俗解释:就像朋友聚餐,必须所有人都到齐了才能开始吃饭。
与CountDownLatch的区别:
-
CountDownLatch是一次性的,CyclicBarrier可重复使用
-
CountDownLatch主线程等待,CyclicBarrier是所有线程互相等待
-
CyclicBarrier可以在所有线程到达后执行一个回调(Runnable)
示例代码:
public class CyclicBarrierDemo {
public static void main(String[] args) {
// 4个线程互相等待
int threadCount = 4;
CyclicBarrier barrier = new CyclicBarrier(threadCount, () ->
System.out.println("所有线程已就位,开始执行下一步"));
for (int i = 0; i < threadCount; i++) {
new Thread(new Worker(barrier), "线程-" + (i + 1)).start();
}
}
static class Worker implements Runnable {
private final CyclicBarrier barrier;
public Worker(CyclicBarrier barrier) {
this.barrier = barrier;
}
@Override
public void run() {
try {
System.out.println(Thread.currentThread().getName() + " 正在执行第一阶段工作");
Thread.sleep((long) (Math.random() * 2000));
System.out.println(Thread.currentThread().getName() + " 到达屏障点,等待其他线程");
barrier.await(); // 等待其他线程
System.out.println(Thread.currentThread().getName() + " 开始执行第二阶段工作");
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
应用场景:
-
多阶段任务,每个阶段需要所有线程完成才能进入下一阶段
-
并行迭代算法
-
模拟多玩家游戏的回合制场景
三、Semphore
Semaphore用于控制同时访问特定资源的线程数量,通过许可证(permits)机制实现。
通俗解释:就像停车场的车位,总共只有N个车位(permits),当车位满了,其他车必须等待有车离开才能进入。
核心方法:
-
Semaphore(int permits)
:构造函数,permits为许可证数量 -
acquire()
:获取许可证,如果没有则阻塞 -
release()
:释放许可证 -
tryAcquire()
:尝试获取许可证,不阻塞
示例代码:
public class SemaphoreDemo {
public static void main(String[] args) {
// 模拟3个打印机
int printerCount = 3;
Semaphore semaphore = new Semaphore(printerCount);
// 10个打印任务
for (int i = 1; i <= 10; i++) {
new Thread(new PrintTask(semaphore), "任务-" + i).start();
}
}
static class PrintTask implements Runnable {
private final Semaphore semaphore;
public PrintTask(Semaphore semaphore) {
this.semaphore = semaphore;
}
@Override
public void run() {
try {
System.out.println(Thread.currentThread().getName() + " 等待打印");
semaphore.acquire(); // 获取许可证
System.out.println(Thread.currentThread().getName() + " 开始打印");
Thread.sleep((long) (Math.random() * 3000)); // 模拟打印时间
System.out.println(Thread.currentThread().getName() + " 打印完成");
semaphore.release(); // 释放许可证
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
应用场景:
-
资源池管理(数据库连接池等)
-
限流控制
-
生产者-消费者问题
四、Phaser
Phaser是Java 7引入的更灵活的阶段同步器,可以替代CountDownLatch和CyclicBarrier,支持动态调整参与线程数。
通俗解释:就像游戏中的多阶段副本,每个阶段都有不同的玩家参与,可以动态加入或退出。
核心特点:
-
动态注册/注销参与方
-
支持分层结构
-
可重用的同步点
-
支持终止机制
使用示例:
public class PhaserDemo {
public static void main(String[] args) {
// 创建Phaser,初始参与方为0
Phaser phaser = new Phaser() {
// 可选的onAdvance方法,在阶段推进前调用
@Override
protected boolean onAdvance(int phase, int registeredParties) {
System.out.println("======= 阶段" + phase + "完成 =======");
return registeredParties == 0; // 返回true表示终止
}
};
// 创建并启动5个任务
for (int i = 0; i < 5; i++) {
// 注册新的参与方
phaser.register();
new Thread(new Task(phaser), "线程-" + (i + 1)).start();
}
}
static class Task implements Runnable {
private final Phaser phaser;
public Task(Phaser phaser) {
this.phaser = phaser;
}
@Override
public void run() {
// 阶段0
System.out.println(Thread.currentThread().getName() + " 执行阶段0工作");
phaser.arriveAndAwaitAdvance(); // 等待其他线程
// 阶段1
System.out.println(Thread.currentThread().getName() + " 执行阶段1工作");
phaser.arriveAndAwaitAdvance();
// 阶段2
System.out.println(Thread.currentThread().getName() + " 执行阶段2工作");
phaser.arriveAndDeregister(); // 完成并注销
}
}
}
应用场景 :
-
复杂多阶段任务处理
-
动态调整参与线程数的场景
五、Exchanger
Exchanger用于两个线程间交换数据,当一个线程调用exchange()方法时,它会等待另一个线程也调用exchange()方法。
通俗解释:就像两个间谍在交换情报,必须两个人都到场才能交换。
示例代码:
public class ExchangerDemo {
public static void main(String[] args) {
Exchanger<String> exchanger = new Exchanger<>();
new Thread(() -> {
try {
String data = "数据A";
System.out.println("线程1准备交换: " + data);
String received = exchanger.exchange(data);
System.out.println("线程1收到: " + received);
} catch (InterruptedException e) {
e.printStackTrace();
}
}).start();
new Thread(() -> {
try {
String data = "数据B";
System.out.println("线程2准备交换: " + data);
String received = exchanger.exchange(data);
System.out.println("线程2收到: " + received);
} catch (InterruptedException e) {
e.printStackTrace();
}
}).start();
}
}
应用场景
-
两个线程间的数据交换
-
管道替代方案
-
校对计算结果
六、总结
-
CountDownLatch:一次性使用,一个或多个线程等待其他线程完成
-
CyclicBarrier:可重复使用,所有线程互相等待
-
Semaphore:控制资源访问数量
-
Phaser:灵活的多阶段同步,支持动态调整
-
Exchanger:两个线程间交换数据