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: 前进,战神金刚!

 

posted @ 2021-08-27 20:08  姚春辉  阅读(65)  评论(0编辑  收藏  举报