Java精通并发-CountDownLatch使用场景与示例分析及底层源码解读

这次准备学习一个平常面试高频次会被提及的东东,属于线程之间的一种通信问题,这里先提一个问题:有两个线程,第一个线程需要等待第二个线程执行完所有的任务之后,第一个线程才能继续执行,那咱们可以使用线程中的哪个机制能实现呢?很显然利用Thread.join()方法,这是人人皆知的,但是还有一种场景是用Thread.join()解决不了的,具体啥场景,下面开始瞅一下,肯定是需要用到CoutDownLatch这个类来解决了。

CoutDownLatch使用场景:

先来看一个场景:启动了一个主服务,然后它下面会启动五个子服务,而当五个子服务都执行到一半的逻辑时【或者是特定条件成立时】则通知主服务可以继续往下执行了,而五个子服务没有执行到一半完成之前主服务只能等待。

很明显这种业务场景用Thread.join()是没法实现的,因为join()是一定要等待指定线程都执行消亡了调用方才能继续往下执行,看一下join()的api描述:

此时解决之道就是CoutDownLatch这个类了,先来简单看一下它的作用:

其中CountDownLatch是由两个单词构成:CoutDown倒数+Latch门闩。

CoutDownLatch示例分析:

接下来则针对上面的场景写一个针对性的示例,来展示一下CoutDownLatch的用法:

这个3代表啥呢?看一下API参数的说明:

其实也就是记数器值的最大大小,继续往下编写:

package com.javacurrency.test5;

import java.util.concurrent.CountDownLatch;
import java.util.stream.IntStream;

public class MyTest1 {
    public static void main(String[] args) {
        CountDownLatch countDownLatch = new CountDownLatch(3);

        IntStream.range(0, 3).forEach(i -> new Thread(() -> {
            try {
                Thread.sleep(2000);

                System.out.println("hello");
            } catch (InterruptedException e) {
                e.printStackTrace();
            } finally {
                countDownLatch.countDown();
            }
        }).start());

        System.out.println("启动子线程完毕!");

        try {
            countDownLatch.await();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        System.out.println("主线程执行完毕!");
    }
}

下面先来看一下执行效果:

下面来分析一下这个程序:

先来对CoutDownLatch的它的特性说明一下:

而当CoutDownLatch中的计数器减为0时则不会再发生变化了,也就是说不会再继续减成负数了,也就是说假如我们创建了四个线程,而计数器设置的是3,最终CountDownLatch的计数器是不会减成-1的,这点需要注意!!那这计数器有啥用呢?其实主要是对这句话产生作用的:

会有啥作用呢?其实当调用coutDownLatch.await()方法时是会检测计数器的值的,当检测到计数器不为0,也就是它大于0,此时调用这个await()方法的线程就会进入阻塞队列当中;而如果计数器等于0了则该await()立马返回。所以基于这样的理论描述,输出结果也能解释了:

那貌似join()也可以对上面代码达到同样的效果呀,这是因为我们在子线程没有添加后续内容,如果说:

陷阱:

这玩意好不好,但是,有坑!!!假如没有调用指定计数器次数的coutDown()方法的话,那么await()方法则会始终阻塞了,啥意思,咱们演示一下:

还有一种可能就是线程数跟计数值是相等的,但是!!!程序这种也容易出坑:

所以最好的写法就是一定要把这个countDown()的执行放到finally中:

CoutDownLatch源码解读:

了解了CountDownLatch的基本使用之后,下面来稍加看一下它的底层实现,有助于加深对它的理解:

先来看一下await()方法:

先大致瞅一下它的说明:

 而它底层是调用了一个sync,看一下:

接下来则来看一下它:

它被子类Sync覆写了:

此时再回到AbstractQueuedSynchronizer.acquireSharedInterruptibly()方法:

而如果计数器为0了,则不会执行它:

而await()方法还有一个超时的重载方法:

跟Object.wait()重载版本类似,咱们来试一下它:

接下来看一下countDown()方法的逻辑:

它的实现又是调用sync里面的方法,跟进去:

它又由子类覆盖了:

从这里也可以看出,不可能出现计数器减到负数的情况,因为到0则就直接返回了,继续:

这样再回到主流程上:

以上就是关于CountDownLatch的核心实现细节,这个类得好好消化,因为实际用到它的可能性还是比较大的。

关注个人公众号,获得实时推送

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

webor2006

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值