java并发编程系列原理篇--JDK中的通信工具类CountDownLatch

前言

jdk中提供了一种能够控制多个线程任务都执行完之后,再执行其他线程任务的一个线程通信工具类,它就是CountDownLatch。下面我们就来看一下它的简单使用以及原理。

CountDownLatch介绍

CountDownLatch,是“闭锁”的一种实现,也被称为“屏障”。它刚开始的状态的是关闭的,并且规定状态开启的条件-即完成一定数量的线程任务后打开。所以常用它来控制某个线程任务运行之前需要完成一些其他若干线程任务的情况下。下面让我们来看一下它的构造函数以及相关方法。

// 构造方法,n代表要完成的线程数
public CountDownLatch(int count)

public void await() // 等待
public boolean await(long timeout, TimeUnit unit) // 超时等待
public void countDown() // count - 1
public long getCount() // 获取当前还有多少count

这几个方法都很好理解,countDown()方法是用在前置线程任务执行完毕时,表示当其中一个前置任务完成了,那么就让这个“屏障”开启所需的线程数量减1。await()方法是主线程任务在执行之前需要调用的方法,用来等待其他前置线程都执行完毕后再执行主线程,同样的它还支持超时等待。下面先让我们看一下它的使用示例。

CountDownLatch的使用

玩游戏的时候,在游戏真正开始之前,一般会等待一些前置任务完成,比如“加载地图数据”,“加载人物模型”,“加载背景音乐”等等。只有当所有的东西都加载完成后,玩家才能真正进入游戏。下面我们就来模拟一下这个demo。

public class CountDownLatchDemo {

    public static void main(String[] args) {
        //假设主任务前有3个前置任务
        CountDownLatch countDownLatch = new CountDownLatch(3);

        //主线程任务
        new Thread(()->{
            try {
                System.out.println("等待数据加载...");
                System.out.println(String.format("还有%d个前置任务", countDownLatch.getCount()));
                countDownLatch.await();
                System.out.println("数据加载完成,正式开始游戏!");
            }catch (InterruptedException e) {
                e.printStackTrace();
            }

        }).start();

        new PreTaskThread("加载地图数据", countDownLatch).start();
        new PreTaskThread("加载任务模型", countDownLatch).start();
        new PreTaskThread("加载背景音乐", countDownLatch).start();

    }


    //主任务的前置任务
    static class PreTaskThread extends Thread{
        private String taskName;
        private CountDownLatch countDownLatch;

        public PreTaskThread(String taskName, CountDownLatch countDownLatch) {
            this.taskName = taskName;
            this.countDownLatch = countDownLatch;
        }

        @Override
        public void run() {
            Random random = new Random();
            try {
                Thread.sleep(random.nextInt(1000));
                System.out.println(taskName + "任务完成");
                //当任务执行完成时,减去一次count
                countDownLatch.countDown();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }

        }
    }
}

运行结果:

等待数据加载...
还有3个前置任务
加载任务模型任务完成
加载背景音乐任务完成
加载地图数据任务完成
数据加载完成,正式开始游戏!

可以从结果中看出来,主线程任务在调用了await()方法后就进入等待状态,开始执行三个前置任务,每个前置任务执行完成之后调用countDown()方法,进行总数减1操作。而三个前置任务的执行顺序如果没有特殊的控制,则是随机执行的

CountDownLatch的原理

其实CountDownLatch类的原理挺简单的,内部同样是一个基层了AQS的实现类Sync,且实现起来还很简单,可能是JDK里面AQS的子类中最简单的实现了,有兴趣的读者可以去看看这个内部类的源码。
需要注意的是构造器中的计数值(count)实际上就是闭锁需要等待的线程数量。这个值只能被设置一次,而且CountDownLatch没有提供任何机制去重新设置这个计数值

文中如有地方写的不妥当或者有疑问,请大家留言,大家相互学习。感谢!

参考链接

深入浅出Java多线程

posted @ 2020-06-16 14:56  爪哇洋  阅读(99)  评论(0编辑  收藏  举报