概述

CountDownLatch 是并发包中的一个工具类,它的典型应用场景为:一个线程等待几个线程执行,待这几个线程结束后,该线程再继续执行。

简单起见,可以把它理解为一个倒数的计数器:初始值为线程数,每个线程结束时执行减 1 操作,当计数器减到 0 时等待的线程再继续执行。

代码分析

CountDownLatch 的类签名和主要方法如下:

public class CountDownLatch {}

常用方法为:await()、await(long, TimeUnit) 和 countDown。其中两个 await 都是让当前线程进入等待状态(获取资源失败);而 countDown 方法是将计数器减去 1,当计数器为 0 的时候,那些处于等待状态的线程会继续执行(获取资源成功)。

构造器代码如下:

private final Sync sync;public CountDownLatch(int count) {if (count < 0) throw new IllegalArgumentException("count < 0");this.sync = new Sync(count);
}

构造器(该构造器是唯一的)传入一个正整数,且初始化了 sync 变量,Sync 是内部的一个嵌套类,继承自 AQS。

await / await(long, TimeUnit):

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

countDown:

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

其中,acquireSharedInterruptibly、tryAcquireSharedNanos 和 releaseShared 都是 AQS 中「共享模式」的方法,具体代码可参考前文「JDK源码分析-AbstractQueuedSynchronizer(3)」的分析。

嵌套类 Sync 代码如下:

private static final class Sync extends AbstractQueuedSynchronizer {private static final long serialVersionUID = 4982264981922014374L;// 构造器,初始化 AQS 的 state 变量Sync(int count) {setState(count);}int getCount() {return getState();}// 尝试获取资源的操作// 只有当 state 变量为 0 的时候才能获取成功(返回 1)protected int tryAcquireShared(int acquires) {return (getState() == 0) ? 1 : -1;}// 尝试释放资源的操作    protected boolean tryReleaseShared(int releases) {// Decrement count; signal when transition to zerofor (;;) {int c = getState();if (c == 0)return false;// 该操作就是尝试把 state 变量减去 1int nextc = c-1;if (compareAndSetState(c, nextc))return nextc == 0;}}
}

Sync 继承了 AQS 抽象类,根据 AQS 可知,acquireSharedInterruptibly 和 tryAcquireSharedNanos 方法的实现都调用了 tryAcquireShared。

流程说明:通常先把 CountDownLatch 的计数器(state)初始化为 N,执行 wait 操作就是尝试以共享模式获取资源,而每次 countDown 操作就是将 N 减去 1,只有当 N 减到 0 的时候,才能获取成功(tryAcquireShared 方法),然后继续执行。

场景举例

为便于理解该类的用法,举两个简单的例子来说明它的使用场景。

场景 1:一个线程等待多个线程执行完之后再继续执行

public void test() throws InterruptedException {int count = 5;// CountDownLatch 的初始化计数器为 5// 注意线程数和计数器保持一致CountDownLatch countDownLatch = new CountDownLatch(count);for (int i = 0; i < count; i++) {int finalI = i;new Thread(() -> {try {TimeUnit.SECONDS.sleep(finalI);} catch (InterruptedException e) {e.printStackTrace();}System.out.println(Thread.currentThread().getName() + " is working ..");// 每个线程执行结束时执行 countDown
            countDownLatch.countDown();}).start();}// 主线程进入等待状态(尝试获取资源,成功后才能继续执行)
    countDownLatch.await();System.out.println(Thread.currentThread().getName() + " go on ..");
}/*  输出结果:Thread-0 is working ..Thread-1 is working ..Thread-2 is working ..Thread-3 is working ..Thread-4 is working ..main go on ..
*/

场景 2:一个线程到达指定条件后,通知另一个线程

private static volatile List<Integer> list = new ArrayList<>();private static void test() {CountDownLatch countDownLatch = new CountDownLatch(1);new Thread(() -> {if (list.size() != 5) {try {// list 的大小为 5 时再继续执行,否则等待// 等待 state 减到 0
        countDownLatch.await();} catch (InterruptedException e) {e.printStackTrace();}}System.out.println(Thread.currentThread().getName() + " start..");}).start();new Thread(() -> {for (int i = 0; i < 10; i++) {list.add(i);System.out.println(Thread.currentThread().getName() + " add " + i);if (list.size() == 5) {// 满足条件时将 state 减 1
        countDownLatch.countDown();}try {TimeUnit.SECONDS.sleep(1);} catch (InterruptedException e) {e.printStackTrace();}}}).start();
}/*  输出结果:Thread-1 add 0Thread-1 add 1Thread-1 add 2Thread-1 add 3Thread-1 add 4Thread-0 start..Thread-1 add 5Thread-1 add 6Thread-1 add 7Thread-1 add 8Thread-1 add 9
*/

小结

CountDownLatch 可以理解为一个倒数的计数器,它的典型应用场景就是一个线程等待几个线程执行结束后再继续执行。其内部是基于 AQS 的共享模式实现的。

相关阅读:

JDK源码分析-AbstractQueuedSynchronizer(3)

Stay hungry, stay foolish.

PS: 本文首发于微信公众号【WriteOnRead】。

转载于:https://www.cnblogs.com/jaxer/p/11317908.html

【JDK】JDK源码分析-CountDownLatch相关推荐

  1. 常用jdk类库源码分析以及各个包

    常用jdk类库源码分析以及各个包 1.java.lang包 java.lang包 是Java中最常用的包,程序不需要注入,就可以使用该包中的类,利用包中的类可以设计最基本的Java程序. 2.java ...

  2. java观察者模式类图_设计模式(十八)——观察者模式(JDK Observable源码分析)...

    1 天气预报项目需求,具体要求以下: 1) 气象站能够将天天测量到的温度,湿度,气压等等以公告的形式发布出去(好比发布到本身的网站或第三方).java 2) 须要设计开放型 API,便于其余第三方也能 ...

  3. Java设计模式——工厂模式讲解以及在JDK中源码分析

    需求:便于手机种类的扩展 手机的种类很多(比如HuaWeiPhone.XiaoMiPhone等) 手机的制作有prepare,production, assemble, box 完成手机店订购功能. ...

  4. JDK源码分析 FutureTask源码分析

    文章目录 前言 一.Callable接口 二.Future接口 三.FutureTask源码分析 3.1 Future继承结构图 3.2 参数介绍 3.3 构造函数 3.4. FutureTask的A ...

  5. JDK源码分析 NIO实现

    总列表:http://hg.openjdk.java.net/ 小版本:http://hg.openjdk.java.net/jdk8u jdk:http://hg.openjdk.java.net/ ...

  6. 单例模式在JDK 应用的源码分析||单例模式注意事项和细节说明

    单例模式在JDK 应用的源码分析 单例模式在JDK 应用的源码分析 1) 我们JDK中,java.lang.Runtime就是经典的单例模式(饿汉式) 2) 代码分析+Debug源码+代码说明 单例模 ...

  7. 【JDK】JDK源码分析-HashMap(1)

    概述 HashMap 是 Java 开发中最常用的容器类之一,也是面试的常客.它其实就是前文「数据结构与算法笔记(二)」中「散列表」的实现,处理散列冲突用的是"链表法",并且在 J ...

  8. jdk源码分析书籍 pdf_什么?Spring5 AOP 默认使用Cglib?从现象到源码深度分析

    推荐阅读: 阿里工作十年拿下P8,多亏了这些PDF陪我成长(Spring全家桶+源码解析+Redis实战等)​zhuanlan.zhihu.com 从入门到熟悉,一步一步带你了解 MySQL 中的「索 ...

  9. 单例模式在JDK应用的源码分析

    单例模式在JDK应用的源码分析 单例模式在jdk中的源码分析 在我们JDK中,java.lang.Runtime就是经典的单例模式(恶汉式) 代码分析+Debug源码+代码说明 public clas ...

最新文章

  1. sql server 2008语言基础: 集合
  2. 【简明教程】windows下xgboost安装到python
  3. 蓝牙解析(part10):BLE ATT/GATT
  4. React ----- 路由懒加载的几种实现方案
  5. CCNA 02 OSI七层
  6. 在Windows Server 2012中配置NAT代理服务器
  7. Struts2 自定义验证器
  8. 来自百度的71款开源项目
  9. 愤怒的小鸟,弹弓效果
  10. “ 鸡尾酒会问题”(cocktail party problem)
  11. C# 在PPT中绘制图表——柱形图、环形图、混合型图表
  12. 2018计算机应用基础教材,2018年计算机应用基础课件全套PPT电子档.ppt
  13. [Camera Drv]Factory mode下camera图像rotate了180度 - MTK物联网在线解答 - 技术论坛
  14. linux 检查系统丢包,Linux 下网络丢包问题处理
  15. RB-tree(红黑树)
  16. 欧拉回路,欧拉路径,欧拉图详解
  17. MySQL——客户端工具简介
  18. MYSQL 那点破事,索引、SQL调优、事务、B+树、分表 ....
  19. 腾讯和华为领导的中国区块链联盟将推出无币区块链
  20. 浏览器与解释器【转】

热门文章

  1. vue打包后图片未显示问题
  2. 类的继承和派生java_Java 类的继承与派生
  3. vue项目中使用lib-flexible解决移动端适配
  4. UHDTV(超高清电视) 的帧率规格:120fps
  5. vb编写各种趣味小程序_【VB小程序】来测测你的打字速度吧
  6. easyui-textbox 和 easyui-validatebox 设置值和获取值
  7. linux端口连通性测试telnet、wget、ssh、curl
  8. 根据一张表更新另一张表
  9. IDEA 编译时 报 “常量字符串过长” 解决办法
  10. MySQL命令行格式化输出