Java并发31:CountDownLatch(下)--两种应用场景
本章主要对CountDownLatch的两种应用场景进行学习。
有关CountDownLatch的基本方法详见上一章:《 Java并发30》
1.用法概述
本人所知的CountDownLatch的用法主要有以下两个方面:
- 开关/哨子(初始count=1):所有调用它的await()方法的线程都会等待,直到开关打开(执行一次countDown())。强调的是同时二字。
- 计数器/总闸(初始count=n):所有调用它的countDown()方法的线程都会使count减1,直到为0,释放所有线程。强调的是多个二字。
下面分别对这两种应用场景进行实例练习。
2.跑步比赛(初始count=1)
场景说明:
- 这是一场在田径赛场举行的跑步比赛。
- 田径赛场共有10个跑道。
- 当10名参赛选手在跑道起点就位后,裁判会倒计时,并最终吹响哨子。
- 当听到哨声之后,10名参赛选手同时起跑。
- 10名参赛选手根据不同的身体素质,耗费不同的时间到达终点。
重点分析:
- 田径赛场共有10个跑道:需要定义一个初始大小为10的线程池。
- 哨子:需要定义一个初始count=1的CountDownLatch对象。
- 并最终吹响哨子:即执行一次countDown()方法。
实例代码:
运动员:
/**
* <p>运动员</p>
*
* @author hanchao 2018/3/28 20:48
**/
static class Player implements Runnable {
private static final Logger LOGGER = Logger.getLogger(Player.class);
/**
* 哨子
*/
CountDownLatch latch;
public Player(CountDownLatch latch) {
this.latch = latch;
}
@Override
public void run() {
String name = Thread.currentThread().getName();
//等待吹哨
LOGGER.info("Player[" + name + "] is waiting for the whistle.");
try {
//注意是await(),不是wait()。
//前置是CountDownLatch的方法,后者是Object的方法。
latch.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
LOGGER.info("Player[" + name + "] is running...");
//跑到终点的时间
Integer time = RandomUtils.nextInt(5000, 9000);
try {
Thread.sleep(time);
} catch (InterruptedException e) {
e.printStackTrace();
}
LOGGER.info("Player[" + name + "] is has arrived the finish line!");
}
}
裁判:
/**
* <p>裁判员</p>
*
* @author hanchao 2018/3/28 20:48
**/
static class Referee implements Runnable {
private static final Logger LOGGER = Logger.getLogger(Referee.class);
/**
* 哨子
*/
CountDownLatch latch;
public Referee(CountDownLatch latch) {
this.latch = latch;
}
@Override
public void run() {
String name = Thread.currentThread().getName();
//准备吹哨
LOGGER.info("Referee[" + name + "] is ready to whistle. 3... 2... 1...!");
//裁判吹哨--countDown()使count减1,当count=0时,所有在latch上await()的线程不再等待
latch.countDown();
}
}
比赛代码:
/**
* <p>倒计时门闩-示例</p>
*
* @author hanchao 2018/3/28 20:52
**/
public static void main(String[] args) throws InterruptedException {
//示例一:跑步比赛---哨子
//创建 哨子
CountDownLatch latch = new CountDownLatch(1);
//创建 10人赛道
int num = 10;
ExecutorService executorService = Executors.newFixedThreadPool(num);
//运动员上赛道
for (int i = 0; i < num; i++) {
executorService.submit(new Player(latch));
}
//裁判准备
Thread.sleep(1000);
//裁判开始吹哨
new Thread(new Referee(latch)).start();
//等所有人都跑完,关闭跑道
Thread.sleep(10000);
executorService.shutdownNow();
}
运行结果:
2018-03-31 22:42:11 INFO CountDownLatchDemo1$Player:46 - Player[pool-1-thread-1] is waiting for the whistle.
2018-03-31 22:42:11 INFO CountDownLatchDemo1$Player:46 - Player[pool-1-thread-2] is waiting for the whistle.
2018-03-31 22:42:11 INFO CountDownLatchDemo1$Player:46 - Player[pool-1-thread-3] is waiting for the whistle.
2018-03-31 22:42:11 INFO CountDownLatchDemo1$Player:46 - Player[pool-1-thread-10] is waiting for the whistle.
2018-03-31 22:42:11 INFO CountDownLatchDemo1$Player:46 - Player[pool-1-thread-9] is waiting for the whistle.
2018-03-31 22:42:11 INFO CountDownLatchDemo1$Player:46 - Player[pool-1-thread-8] is waiting for the whistle.
2018-03-31 22:42:11 INFO CountDownLatchDemo1$Player:46 - Player[pool-1-thread-7] is waiting for the whistle.
2018-03-31 22:42:11 INFO CountDownLatchDemo1$Player:46 - Player[pool-1-thread-6] is waiting for the whistle.
2018-03-31 22:42:11 INFO CountDownLatchDemo1$Player:46 - Player[pool-1-thread-5] is waiting for the whistle.
2018-03-31 22:42:11 INFO CountDownLatchDemo1$Player:46 - Player[pool-1-thread-4] is waiting for the whistle.
2018-03-31 22:42:12 INFO CountDownLatchDemo1$Referee:86 - Referee[Thread-0] is ready to whistle. 3... 2... 1...!
2018-03-31 22:42:12 INFO CountDownLatchDemo1$Player:54 - Player[pool-1-thread-1] is running...
2018-03-31 22:42:12 INFO CountDownLatchDemo1$Player:54 - Player[pool-1-thread-6] is running...
2018-03-31 22:42:12 INFO CountDownLatchDemo1$Player:54 - Player[pool-1-thread-4] is running...
2018-03-31 22:42:12 INFO CountDownLatchDemo1$Player:54 - Player[pool-1-thread-5] is running...
2018-03-31 22:42:12 INFO CountDownLatchDemo1$Player:54 - Player[pool-1-thread-7] is running...
2018-03-31 22:42:12 INFO CountDownLatchDemo1$Player:54 - Player[pool-1-thread-8] is running...
2018-03-31 22:42:12 INFO CountDownLatchDemo1$Player:54 - Player[pool-1-thread-9] is running...
2018-03-31 22:42:12 INFO CountDownLatchDemo1$Player:54 - Player[pool-1-thread-10] is running...
2018-03-31 22:42:12 INFO CountDownLatchDemo1$Player:54 - Player[pool-1-thread-2] is running...
2018-03-31 22:42:12 INFO CountDownLatchDemo1$Player:54 - Player[pool-1-thread-3] is running...
2018-03-31 22:42:17 INFO CountDownLatchDemo1$Player:62 - Player[pool-1-thread-9] is has arrived the finish line!
2018-03-31 22:42:18 INFO CountDownLatchDemo1$Player:62 - Player[pool-1-thread-8] is has arrived the finish line!
2018-03-31 22:42:18 INFO CountDownLatchDemo1$Player:62 - Player[pool-1-thread-6] is has arrived the finish line!
2018-03-31 22:42:18 INFO CountDownLatchDemo1$Player:62 - Player[pool-1-thread-10] is has arrived the finish line!
2018-03-31 22:42:19 INFO CountDownLatchDemo1$Player:62 - Player[pool-1-thread-7] is has arrived the finish line!
2018-03-31 22:42:19 INFO CountDownLatchDemo1$Player:62 - Player[pool-1-thread-2] is has arrived the finish line!
2018-03-31 22:42:19 INFO CountDownLatchDemo1$Player:62 - Player[pool-1-thread-5] is has arrived the finish line!
2018-03-31 22:42:20 INFO CountDownLatchDemo1$Player:62 - Player[pool-1-thread-4] is has arrived the finish line!
2018-03-31 22:42:20 INFO CountDownLatchDemo1$Player:62 - Player[pool-1-thread-1] is has arrived the finish line!
2018-03-31 22:42:21 INFO CountDownLatchDemo1$Player:62 - Player[pool-1-thread-3] is has arrived the finish line!
3.战神金刚(初始count=n)
场景说明:
- 模拟儿时看到一部动画片《战神金刚》的变身过程。
- 战神金刚有五个机器狮子组成,这五个机器狮子可以变形成战神金刚身体的一部分:腿、脚、躯干、手臂、头。
- 当战神金刚的身体组装完成在一起之后,会大喊:前进,战神金刚!
- 原版口号:组成腿和脚,组成躯干和手臂, 我来组成头部!
-->
前进,战神金刚! - 程序版口号:我来组成[手臂]!我来组成[头部]!我来组成[脚部]!我来组成[躯干]!我来组成[腿部]!
-->
前进,战神金刚!
重点分析:
- 战神金刚有五个机器狮子组成:需要定义一个初始大小为5的线程池。
- 组装完成之后,会大喊:需要定义一个初始count=5的CountDownLatch对象。
- 组装:每个部件的组装,即执行一次countDown()方法。
实例代码:
机器狮子:
/**
* <p>机器狮子</p>
* @author hanchao 2018/3/31 23:02
**/
static class MachineLion implements Runnable {
private static final Logger LOGGER = Logger.getLogger(MachineLion.class);
//身体部分
private String name;
//变形计数器
CountDownLatch latch;
public MachineLion(String name, CountDownLatch latch) {
this.name = name;
this.latch = latch;
}
@Override
public void run() {
//花费一些时间进行组装
Integer time = RandomUtils.nextInt(0, 2000);
LOGGER.info(Thread.currentThread().getName() + " [" + name + "] 正在进行组装...");
try {
Thread.sleep(time);
} catch (InterruptedException e) {
e.printStackTrace();
}
latch.countDown();
LOGGER.info(Thread.currentThread().getName() + " 我来组成[" + name + "]!");
}
}
战神金刚进行变身:
/**
* <p>CountDownLatch用法2-线程计数器-战神金刚</p>
*
* @author hanchao 2018/3/28 21:34
**/
public static void main(String[] args) throws InterruptedException {
//main就是战神金刚
int num = 5;
//定义 变形计数器
CountDownLatch latch = new CountDownLatch(num);
//定义 线程池
ExecutorService executorService = Executors.newFixedThreadPool(num);
//五个机器狮子纷纷开始组装
executorService.submit(new MachineLion("脚部", latch));
executorService.submit(new MachineLion("腿部", latch));
executorService.submit(new MachineLion("躯干", latch));
executorService.submit(new MachineLion("手臂", latch));
executorService.submit(new MachineLion("头部", latch));
//等待五个机器狮子进行组装
LOGGER.info(Thread.currentThread().getName() + " [战神金刚] 正在等待组装...");
try {
latch.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
Thread.sleep(100);
//战神金刚开始发威
LOGGER.info(Thread.currentThread().getName() + ": 前进,战神金刚!");
executorService.shutdownNow();
}
运行结果:
2018-03-31 22:59:52 INFO CountDownLatchDemo2:75 - main [战神金刚] 正在等待组装...
2018-03-31 22:59:52 INFO CountDownLatchDemo2$MachineLion:45 - pool-1-thread-2 [腿部] 正在进行组装...
2018-03-31 22:59:52 INFO CountDownLatchDemo2$MachineLion:45 - pool-1-thread-4 [手臂] 正在进行组装...
2018-03-31 22:59:52 INFO CountDownLatchDemo2$MachineLion:45 - pool-1-thread-3 [躯干] 正在进行组装...
2018-03-31 22:59:52 INFO CountDownLatchDemo2$MachineLion:45 - pool-1-thread-5 [头部] 正在进行组装...
2018-03-31 22:59:52 INFO CountDownLatchDemo2$MachineLion:45 - pool-1-thread-1 [脚部] 正在进行组装...
2018-03-31 22:59:53 INFO CountDownLatchDemo2$MachineLion:52 - pool-1-thread-5 我来组成[头部]!
2018-03-31 22:59:53 INFO CountDownLatchDemo2$MachineLion:52 - pool-1-thread-4 我来组成[手臂]!
2018-03-31 22:59:53 INFO CountDownLatchDemo2$MachineLion:52 - pool-1-thread-1 我来组成[脚部]!
2018-03-31 22:59:54 INFO CountDownLatchDemo2$MachineLion:52 - pool-1-thread-2 我来组成[腿部]!
2018-03-31 22:59:54 INFO CountDownLatchDemo2$MachineLion:52 - pool-1-thread-3 我来组成[躯干]!
2018-03-31 22:59:54 INFO CountDownLatchDemo2:83 - main: 前进,战神金刚!