CountDownLatch源码分析

CountDownLatch类,从jdk1.5引入;是提供给开发的计数器。
如下给出示例:

package test;

import java.util.concurrent.CountDownLatch;

public class CountDownLatchRunner {

    public static void main(String[] args) throws InterruptedException {
        CountDownLatch countDownLatch = new CountDownLatch(2);

        //错误的位置;写在这里,程序会走不下去的
        //countDownLatch.await();

        for(int i = 0; i < 2; i++) {
            new Thread(() -> {
                System.out.println("计数器做减法!");
                countDownLatch.countDown();
            }).start();
        }

        countDownLatch.await();
        System.out.println("计数器减为0了,后续。。。");
    }

}

输出结果:
在这里插入图片描述

源码分析(基于jdk1.8)

CountDownLatch是基于AQS(指的是AbstractQueuedSynchronizer这个抽象类)共享模式;其内部定义了Sync类,继承自AQS,重写了tryAcquireShared(int acquires)、tryReleaseShared(int releases)这2个方法。
tryAcquireShared(int acquires)方法如下:
在这里插入图片描述

判断计数器的值是否为0,为0返回1,不为0返回-1。


tryReleaseShared(int releases)方法如下:
在这里插入图片描述

判断计数器的值是否为0,为0返回false,不为0将值减1并更新,再将更新后的值和0比较,为0返回true,否则返回false。
注:更新时,循环+compareAndSetXXX(),就是使用cas机制保证多线程的安全。


我们就分析示例代码,

CountDownLatch的构造函数:

设置计数器的值为传入的值。
在这里插入图片描述


在这里插入图片描述



在这里插入图片描述

CountDownLatch的await()方法:

只有一行代码:调用AQS的acquireSharedInterruptibly(int arg),
在这里插入图片描述

点进去看,
在这里插入图片描述
里面调用了Sync的tryAcquireShared(int acquires),如果返回值大于等于0(翻译一下,就是计数器的值为0),不做处理,否则调用AQS的doAcquireSharedInterruptibly(int arg);我们再看这个方法,
在这里插入图片描述

先去调用addWaiter(Node node),
在这里插入图片描述
方法中先new了一个Node对象(起个名字:对象a),对象a的thread属性为当前线程对象,nextWaiter属性为Node.SHARED对象,然后将pred变量赋值为tail对象,此时因为tail对象为null,进入到enq(Node node)方法中,在这里插入图片描述
此方法会把head对象初始化好,赋值为一个new Node(),也会把tail对象赋值为对象a,此时enq(Node node)执行完,addWaiter(Node node)也执行完,返回对象a;doAcquireSharedInterruptibly(int arg)继续往下看,开始执行循环,拿到对象a的prev属性去和head对象比较,此时是相等的,进入到if中,去调用Sync的tryAcquireShared(int acquires),此时计数器的值不为0,不进入下一个if,往下执行,调用shouldParkAfterFailedAcquire(Node pred, Node node)方法中,
在这里插入图片描述
因为head对象的waitSatus为0(int属性,默认为0),此时赋值为Node.SIGNAL(-1),再返回false;此时条件未满足,再次执行循环,当再次执行shouldParkAfterFailedAcquire(Node pred, Node node)方法时,因为head对象的waitStatus为-1,返回true,会接着执行parkAndCheckInterrupt()方法,
在这里插入图片描述

执行到LockSupport#park(Object blocker)方法时,线程会阻塞,不再执行后续代码。


CountDownLatch的countDown()方法:

也是只有一行代码,
在这里插入图片描述

调用AQS的releaseShared(int arg),点进去看,在这里插入图片描述

调用了Sync的tryReleaseShared(int releases),如果返回值为false(翻译一下,就是计数器的值为0或减去1后不为0),代码就执行完毕,否则(计数器的值减去1后为0)执行doReleaseShared()方法,细看这个方法,在这里插入图片描述
执行循环,判断head对象是否为null并且head和tail对象是否相等,此时2个条件都满足,进入到if里,判断head对象的waitStatus属性,此时该值为Node.SIGNAL,接着进入到if里,利用cas机制把head对象的waitStatus属性更新为0,然后接着执行unparkSuccessor(Node node),在这里插入图片描述

此时因为head对象有后继节点(next属性为上面的对象a),此时655行的if不执行,执行661行的if,调用LockSupport#unpark(Thread thread)方法,该方法会把上述阻塞的线程恢复,然后unparkSuccessor(Node node)执行完毕,接着往下执行,695行的if未满足(经作者调试后发现,unparkSuccessor(Node node)执行完后,head对象会赋值为上面的对象a,head、tail对象是同一个),再来一次循环,此时head对象不为null,但是head对象和tail对象相等,会执行695行的if,此时终止循环,doReleaseShared()方法执行完毕,countDown()方法也同样执行完毕。


最后,欢迎志同道合的朋友来评论区讨论。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值