文章目录
- 一、CountDownLatch
- 二、CyclicBarrier
- 三、Semaphore
- 四、Phaser
提示:以下是本篇文章正文内容,下面案例可供参考
一、CountDownLatch
CountDownLatch如同火箭发射,计数只能不断减减,当到达0时即发射
场景示例:考场中有多个同学考试,每个同学写完试卷后,将试卷交给老师即可离开,老师需要收齐所有人的试卷后才能离开。
代码如下(示例):
public class Test {
public static void main(String[] args) throws InterruptedException {
// CountDownLatch 倒计时到0,发射(减法)
// 考场学生人数为 6(一旦定下就不能在增加)
CountDownLatch countDownLatch = new CountDownLatch(6);
for (int i = 1; i <= 6; i++) {
new Thread(() -> {
try {
// 模拟每个学生完成试卷的时间不同(线程休眠随机时间)
Thread.sleep(new Random().nextInt(3000));
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + "\t同学写完,离开教室");
// 上交试卷给老师(走一个,计数减少一个)
countDownLatch.countDown();
}, i + "号考生").start();
}
// 主线程相当于老师,需要等所有的人交卷,才能离开(阻塞当前线程,等待所有线程完成任务,即 countDown)
countDownLatch.await();
Thread.currentThread().setName("老师");
System.out.println(Thread.currentThread().getName() + "\t收齐试卷,关门走人");
}
}
输出结果:
二、CyclicBarrier
CyclicBarrier如同游戏匹配,不凑齐人数,就一直在匹配界面,达到指定人数后才能开始对战
代码如下(示例):
public class Test {
public static void main(String[] args) throws InterruptedException {
// CyclicBarrier 指定值达到 0,发射
// 需要十个人才能开始游戏,(指定个数,之后不能改变,满足条件后,自动执行 Runnable 方法)
CyclicBarrier cyclicBarrier = new CyclicBarrier(10, () -> {
System.out.println("十个人已经全部确认,开始游戏");
});
for (int i = 1; i <= 10; i++) {
final int tmpInt = i;
new Thread(() -> {
try {
// 模拟玩家匹配时间
Thread.sleep(new Random().nextInt(3000));
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + "\t已经确认");
try {
// 等待其他玩家准备(阻塞等待,等待人数减 1,减到 0,就发车)
cyclicBarrier.await();
} catch (InterruptedException | BrokenBarrierException e) {
e.printStackTrace();
}
}, tmpInt + "号玩家").start();
}
}
}
输出结果:
三、Semaphore
Semaphore如同一个停车场,里面的车位数是固定的,当停满车后,必须有车出来才能再进
Semaphore 主要用于两个目的,一个是用于多个共享资源的互斥使用,另一个用于并发线程数的控制。
涉及到并发就有有两种模式:
- 非公平(谁抢到是谁的),默认是非公平的
public Semaphore(int permits) {
sync = new NonfairSync(permits);
}
- 公平(先来后到),当 fair 为 true 是公平的
public Semaphore(int permits, boolean fair) {
sync = fair ? new FairSync(permits) : new NonfairSync(permits);
}
代码如下(示例):
public class Test {
public static void main(String[] args) throws InterruptedException {
// 一共 3 个停车位(资源数开始时固定)
Semaphore semaphore = new Semaphore(3);// 默认非公平(抢占式)
// 模拟 6 辆汽车要停车
for (int i = 1; i <= 6; i++) {
new Thread(() -> {
try {
// 询问还有没有停车位,有就进去,没有等待(获得资源)
semaphore.acquire();
System.out.println(Thread.currentThread().getName() + "\t抢到车位");
// 模拟停车时间
Thread.sleep(new Random().nextInt(3000));
System.out.println(Thread.currentThread().getName() + "\t事情办完,离开车位");
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
// 办完事情,开车离开(释放资源)
semaphore.release();
}
}, i + "号车主").start();
}
}
}
输出结果:
四、Phaser
Phaser是JDK 7新增的一个同步辅助类,它可以实现CyclicBarrier和CountDownLatch类似的功能,而且它支持对任务的动态调整,并支持分层结构来达到更高的吞吐量。
CountDownLatch 和 CyclicBarrier 在最初就固定了线程的数量,而且中途不可改变,当完成指定的任务后,就不能再次使用,Phaser 就解决了该问题。
class player implements Runnable {
private final Phaser phaser;
player(Phaser phaser) {
this.phaser = phaser;
}
@Override
public void run() {
try {
// 第一阶段——确定参赛的运动员人数(等待创建好所有线程再开始)
phaser.arriveAndAwaitAdvance();
// 第二阶段——等待所有选手都做好准备,发令枪再开始
Thread.sleep(new Random().nextInt(3000));// 模拟热身准备
System.out.println(Thread.currentThread().getName() + "号运动员准备完毕");
phaser.arriveAndAwaitAdvance();
// 第三阶段——所有运动员开始跑步
Thread.sleep(new Random().nextInt(3000));// 模拟跑步时间
System.out.println(Thread.currentThread().getName() + "号运动员冲线!");
// 冲线后结束(线程下线注销)
phaser.arriveAndDeregister();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
public class Test {
public static void main(String[] args) throws InterruptedException {
// 模拟了 100 米赛跑,10 名运动员,只等裁判一声令下。当所有人都到达终点时,比赛结束。
// 设置初始的注册人数(1 个裁判)
final Phaser phaser = new Phaser(1);
// 十名运动员
for (int index = 0; index < 10; index++) {
// 每个运动员都注册到其中,即确定参加比赛(动态增加,复用)
phaser.register();
new Thread(new player(phaser), index + "号运动员").start();
}
System.out.println("比赛开始");
// 裁判下线,注销当前线程,比赛开始
phaser.arriveAndDeregister();
// 所有运动员是否到达终点,没有就一直等待
while (!phaser.isTerminated()) {
}
System.out.println("所有运动员到达终点,比赛结束");
}
}
输出结果: