前言

原本是昨天分享countDownLatch相关知识点的,但是昨天粗略看写了,发现自己对countDownLatch的认知还不够,所以就半道分享了常用的三种多线程线程安全解决方案的性能比较,虽然过程中翻车了,但是还是有收获的,也不亏。今天又去看了下count的相关知识,然后做了一个小demo,感觉有点眉目了,所以我们来继续看countDownLatch。

countDownLatch

countDownLath是jdk1.5引入的一个新特性,它的出现主要是为了解决某些应用场景下多线程运行顺序的问题。我们在定义countDownLatch的时候,需要指定它的count大小,它就是通过这个count来控制多线程运行顺序的。

它有两个核心的方法countDown和await,countDown的作用是当线程执行完后把count减一,只能用一次;await方法是判断count是否减为0,这个方法本身是阻塞的,如果count不是0,await后面的代码是不会被执行的。

接下来,我们通过一段示例代码来看下countDownLatch的基本用法:

public class Example {
    private static AtomicInteger count = new AtomicInteger(0);
    private static final int SIZE_FIRST = 100;
    private static final int SIZE_SECOND = 50;

    public static void main(String[] args) throws InterruptedException {
        ExecutorService executorService = Executors.newCachedThreadPool();
        final CountDownLatch countDownLatch = new CountDownLatch(SIZE_FIRST);
        for (int i = 0; i < SIZE_FIRST; i++) {
            executorService.execute(new Task1(countDownLatch));
        }
        countDownLatch.await();
        final CountDownLatch countDownLatch2 = new CountDownLatch(SIZE_SECOND);
        for (int i = 0; i < SIZE_SECOND; i++) {
            executorService.execute(new Task2(countDownLatch2));
        }
        countDownLatch2.await();
        for (int i = 0; i < SIZE_SECOND; i++) {
            executorService.execute(new Task3());
        }
        executorService.shutdown();
    }


    static class Task1 implements Runnable {
        private final CountDownLatch countDownLatch;

        Task1(CountDownLatch countDownLatch) {
            this.countDownLatch = countDownLatch;
        }


        @Override
        public void run() {
            System.out.println("Task1: " + count.getAndIncrement());
            this.countDownLatch.countDown();
        }
    }

    static class Task2 implements Runnable {
        private final CountDownLatch countDownLatch;

        public Task2(CountDownLatch countDownLatch) {
            this.countDownLatch = countDownLatch;
        }

        @Override
        public void run() {
            System.out.println("Task2: " + count.getAndIncrement());
            this.countDownLatch.countDown();
        }
    }

    static class Task3 implements Runnable {
        @Override
        public void run() {
            System.out.println("Task3: " + count.getAndIncrement());
        }
    }

}

这里我分别定义了三个线程,前两个线程构造的时候都需要传入countDownLatch,然后在run方法的尾部执行countDown方法,也就是每启动一个线程,count减一。

我们这里还定义了两个countDownLatch,初始值分别是100和50,他们分别对应线程task1和task2的执行次数。

在线程task1和task2之间我们执行第一个countDownLatch的await方法,控制线程task1和task2的执行顺序,确保线程task1先执行;

在线程task2和线程task3之间我们执行第二个countDownLatch的await方法,控制线程task2和task3的执行顺序,确保线程task2先执行。

然后,我们运行上面的代码,会得到如下结果:

多线程之countDownLatch_ide

多线程之countDownLatch_应用场景_02

在以上结果中,我们可以看到,不论你执行多少次,线程运行顺序都是task1、task2、task3,当然await方法之前的执行顺序我们是没有办法控制的。

总结

从上面演示结果来看,countDownLatch主要是用来控制多线程运行顺序的,特别适合多个线程协同运行但是有顺序要求的业务,更多应用场景大家可以好好研究,我们今天的内容就到这里吧!