首页 > 编程语言 >CountDownLatch闭锁源码解析(基于jdk11)

CountDownLatch闭锁源码解析(基于jdk11)

时间:2022-12-06 10:58:03浏览次数:50  
标签:AQS await jdk11 state 源码 线程 CountDownLatch 方法

目录

CountDownLatch闭锁源码解析(基于jdk11)

1.1 CountDownLatch概述

public class CountDownLatch extends Object

CountDownLatch是一种同步工具,常被称为"闭锁",也叫做"倒计数器"。在完成一组正在其他线程中执行的操作之前,CountDownLatch允许一个或多个线程一直等待。

很明显,这类似于在开始某个行为之前的准备操作

比如有一个任务A,它要等待其他4个任务完成后才能执行后续工作,此时就可以利用CountDownLatch。

1.2 CountDownLatch原理

1.2.1 基本结构(jdk11)

image-20221013103227984

UML类图可知,CountDownLatch内部同样也使用了AQS实现功能,大胆猜测与AQS里的state状态属性有关。

CountDownLatch的构造函数接受了一个int类型的count参数作为计数器,如果你想等待N个线程计数,那就传入N。通过构造函数,实际上是把count赋值给了AQS里的同步状态属性state。

1、image-20221013104059600

2、image-20221013104145324

3、image-20221013104203970

在CountDownLatch的Sync实现中,重写了tryAcquireShared和tryReleaseShared方法,此处可看出是一个共享锁。

一般情况下(常见Lock锁的实现中)我们在释放锁的时候会将state资源减少,获得锁的时候会将state资源增加,当state变为0表示释放锁成功或者没有线程获取到锁,但是CountDownLatch中state的含义则不一样:

  1. 在尝试获取锁的tryAcquireShared中,虽然名字叫获取锁,但事里面逻辑却只做了一个判断,如果state为0就表示获得了锁,state为其他值的情况下都没有获取锁,tryAcquireShared方法在awit()系列方法中被调用。
  2. 在尝试释放锁的tryReleaseShared方法中,虽然名字叫释放锁,但却仅仅是在对state尝试自减操作,它的内部是一个循环操作,每一次的调用tryReleaseShared都会首先判断state是否为0,如果是,那么返回false表示“释放锁失败”,如果不是那么尝试CAS的将state自减1,CAS成功之后会判断此时的值是否为0,如果不是那么表示“释放锁失败”,返回false,否则表示“释放锁成功”,返回true,这里的操作可以永远保证只有一个线程能够因为“释放锁成功”而返回true。tryReleaseShared方法在countDown()方法中被调用。

image-20221013111554267

1.2.2 await()方法

 public void await() throws InterruptedException {
        sync.acquireSharedInterruptibly(1);
    }

需要等待的线程调用。调用该方法后,当前线程会被阻塞,直到下面的情况之一发生才会返回:

  1. 当计数器的值为0 时;
  2. 其他线程调用了当前线程的interrupt()方法中断了当前线程,当前线程就会抛出InterruptedException 异常,然后返回。

根据源码,想要调用await方法的线程能够返回,一般情况下需要获取到共享锁,而CountDownLatch内部的tryAcquireShared返回大于0的要求是state为0,即只有在state为0的时候,调用await方法的线程才能能够返回。

/**
 * CountDownLatch 的await方法
 *
 * @throws InterruptedException 等待时被中断
 */
public void await() throws InterruptedException {
    //调用了AQS 的acquireSharedInterruptibly方法,共享式可中断获取锁
    sync.acquireSharedInterruptibly(1);
}

/**
 * AQS 的acquireSharedInterruptibly方法
 * 共享式获取同步状态,可以被中断,在AQS部分我们已经讲过了
 *
 * @param arg 参数,在实现的时候可以传递自己想要的数据,这里没什么用
 * @throws InterruptedException 等待时被中断
 */
public final void acquireSharedInterruptibly(int arg)
        throws InterruptedException {
    //如果线程被中断则抛出异常
    if (Thread.interrupted())
        throw new InterruptedException();
    //tryAcquireShared方法由AQS的子类实现,尝试共享式获取锁,如果返回值小于0,表示获取失败
    if (tryAcquireShared(arg) < 0)
        //获取锁失败的线程进入AQS的队列等待,在被唤醒之后还是会继续调用tryAcquireShared获取锁,直到获得锁成功
        doAcquireSharedInterruptibly(arg);
}

1.2.3 await(timeout, unit)方法

public boolean await(long timeout, TimeUnit unit)
        throws InterruptedException {
        return sync.tryAcquireSharedNanos(1, unit.toNanos(timeout));
    }

需要等待的线程调用。调用该方法后,当前线程会被阻塞,直到下面的情况之一发生才会返回:

  1. 当计数器值为0 时,这时候会返回true ;
  2. 设置的timeout 时间到了,因为超时而返回false ;
  3. 其他线程调用了当前线程的interrupt()方法中断了当前线程,当前线程就会抛出InterruptedException 异常,然后返回。

与空参await()方法类似,内部使用了AQS的tryAcquireSharedNanos,而空参await()则是使用acquireSharedInterruptibly

/**
 * CountDownLatch 的await( timeout, unit)方法
 * 超时等待
 *
 * @param timeout 等待时间
 * @param unit    时间单位
 * @return true 成功 false 失败
 * @throws InterruptedException 被中断
 */
public boolean await(long timeout, TimeUnit unit)
        throws InterruptedException {
    //调用了AQS 的tryAcquireSharedNanos方法,共享式超时可中断获取锁
    return sync.tryAcquireSharedNanos(1, unit.toNanos(timeout));
}

/**
 * AQS 的tryAcquireSharedNanos方法
 * 共享式超时获取锁,可以被中断,在AQS部分我们已经讲过了
 *
 * @param arg          参数
 * @param nanosTimeout 超时时间,纳秒
 * @return 是否获取锁成功
 * @throws InterruptedException 被中断
 */
public final boolean tryAcquireSharedNanos(int arg, long nanosTimeout)
        throws InterruptedException {
    //最开始就检查一次,如果当前线程是被中断状态,直接抛出异常
    if (Thread.interrupted())
        throw new InterruptedException();
    //下面是一个||运算进行短路连接的代码
    //tryAcquireShared尝试获取锁,获取到了(返回大于等于0)直接返回true
    //获取不到(左边表达式为false) 就执行doAcquireSharedNanos方法
    //doAcquireSharedNanos等待一段时间,直到途中计数器变成了0就返回,或者时间到了自动返回,或者等待时被中断
    return tryAcquireShared(arg) >= 0 ||
            doAcquireSharedNanos(arg, nanosTimeout);
}

1.2.4 countDown()方法

public void countDown() {
        sync.releaseShared(1);
    }

需要准备线程调用。如果当前计数(也就是state)等于0,则什么也不做;

如果当前计数大于0,则尝试CAS将计数器递减1,递减成功如果新的计数为零,出于线程调度目的,将唤醒所有的因为调用await而等待的线程。

底层使用AQS的tryReleaseShared方法

1.2.5 countDown()方法

public long getCount() {
        return sync.getCount();
    }

获取当前计数器的值,也就是AQS 的state 的值。

1.3 CountDownLatch的使用

具体看语雀另一篇CountDownLatch文章

1.4 CountDownLatch的总结

CountDownLatch利用AQS状态属性state来实现共享锁

在tryAcquireShared中只有state为0才表示"获取到锁",否则就会阻塞调用线程,在await方法使用;

在tryReleaseShared中只有state自减后为0才表示释放到锁,即只有当某个countDown方法将state变成0的时候,此时表示“成功释放了锁”,随后就会唤醒因为调用await方法而阻塞的线程,被唤醒的线程会判断到此时state=0,因此可以返回

countDown方法可以用在任何地方,这里的初始值N,可以是N个线程执行完毕之后调用N次countDown方法,也可以是1个线程里的N次调用countDown方法。

CountDownLatch一般用来确保某些活动直到其他活动都完成才继续执行,比如:

  1. 确保某个计算在其需要的所有资源都被初始化之后才继续执行;
  2. 确保某个服务在其依赖的所有其他服务都已经启动之后才启动;
  3. 等待直到某个操作所有参与者都准备就绪再继续执行。

标签:AQS,await,jdk11,state,源码,线程,CountDownLatch,方法
From: https://www.cnblogs.com/checkcode/p/16954560.html

相关文章

  • python爬虫爬取网易云音乐(超详细教程,附源码)
    一、前言先说结论,目前无法下载无损音乐,也无法下载vip音乐。此代码模拟web网页js加密的过程,向api接口发送参数并获取数据,仅供参考学习,如果需要下载网易云音乐,不如直接在......
  • C++学习---cstdbool和cstddef源码学习分析
    引言cstdbool是C++对stdbool.h头文件的封装,里面定义了与bool变量相关的宏;cstddef是C++对stddef.h头文件的封装,里面定义一些特殊类型(如size_t),有用的宏函数(offsetof)。平时我们......
  • php版高校宿舍管理系统源码(文末有下载方式)
    大家好,我是程序猿零壹。分享一款高校宿舍管理系统,该系统使用php+mysql开发,实现了学生宿舍管理的功能模块,包含学生管理、楼宇管理、宿舍管理、宿舍分配,物品出入登记、来访......
  • Spring源码-03-容器创建
    Spring源码-03-容器创建注解Bean方式publicclassAnnotationCtxMain02{ publicstaticvoidmain(String[]args){ newAnnotationConfigApplicationContext(MyCf......
  • Spring源码-02-Bean容器
    Spring源码-02-Bean容器一类关系二宏观视角......
  • Spring源码-04-注解Bean读取器
    Spring源码-04-注解Bean读取器AnnotatedBeanDefinitionReader一构造方法publicAnnotatedBeanDefinitionReader(BeanDefinitionRegistryregistry,Environmentenviro......
  • DataX源码分析一:环境搭建
    最近工作中使用到了DataX工具,比较肤浅的看了看源码,做一个记录。比较肤浅的探讨,各位莫见笑。 【1】获取源码源码地址(github):[email protected]:alibaba/DataX.git源码地址(g......
  • @Import源码翻译
     类上方注释指示要导入的一个或多个组件类——通常是@Configuration类。提供等同于SpringXML中的<import/>元素的功能。允许导入@Configuration类、ImportSel......
  • @EnableAsync源码注释翻译
     类上方注释启用Spring的异步方法执行能力,类似于Spring的<task:*>XML命名空间中的功能。与@Configuration类一起使用,如下所示,为整个Spring应用程序上下文启用注解......
  • 直播系统app源码,自定义九宫格,计算器布局,验证码认证
    直播系统app源码,自定义九宫格,计算器布局,验证码认证1、先写几个接收验证码的文本框 returnScaffold(   backgroundColor:ColorsUtil.hexStringColor("#B1B1B1")......