一、CountDownLatch 概述

1.1 什么是 CountDLatch

闭锁(CountDownLatch)是 java.util.concurrent 包下的一种同步工具类。闭锁可以用来确保某些活动直到其他活动都完成后才执行。

闭锁相当于一扇门:在闭锁到达结束状态之前,这扇门一直是关闭的,并且没有任何线程能通过,当达到结束状态时,这扇门会打开,并允许所有的线程通过。

1.2 CountDownLatch 的应用场景

  • 确保某个计算在其需要的所有资源都被初始化之后才执行
  • 确保某个服务在其依赖的所有其他服务都已经启动之后才启动
  • 等待直到每个操作的所有参与者都就绪再执行(比如打麻将时需要等待四个玩家就绪)

1.3 CountDownLatch 简单应用

我们知道 4 个人玩纸牌游戏一定会先等所有玩家就绪后才会发牌,下面我们就来用闭锁简单的模拟一下。

public class CountDownLatchTest {/*** 初始化需要等待的 4 个事件*/private static CountDownLatch latch = new CountDownLatch(4);public static void main(String[] args) throws InterruptedException {// 创建 4 个线程分别代表 4 个玩家new Thread(() -> { System.out.println("玩家 1 已就绪"); latch.countDown(); }).start();new Thread(() -> { System.out.println("玩家 2 已就绪"); latch.countDown(); }).start();new Thread(() -> { System.out.println("玩家 3 已就绪"); latch.countDown(); }).start();new Thread(() -> { System.out.println("玩家 4 已就绪"); latch.countDown(); }).start();// 所有玩家就绪前一直阻塞latch.await();System.out.println("所有玩家已就绪,请发牌");}
}

下面是控制台输出:

二、CountDownLatch 原理分析

CountDownLatch 底层是基于 AQS 实现的,如果不懂 AQS 原理的小伙伴需要先了解下 AQS 再来看这篇文章。

2.1 API 相关方法

构造函数:

    public CountDownLatch(int count) {if (count < 0) throw new IllegalArgumentException("count < 0");// 初始化 count 值this.sync = new Sync(count);}

CountDownLatch 内部有一个 Sync 同步对象,这个对象是一个内部类实现了 AQS,下面我们会具体来看方法实现。

await 方法:

    public void await() throws InterruptedException {// 共享式检查是否中断,如果中断抛出异常// 调用 tryAcquireShared 方法尝试获取同步状态,当闭锁内的线程执行完毕后尝试获取成功,直接返回sync.acquireSharedInterruptibly(1);}

countDown 方法:

    public void countDown() {// 调用 releaseShared 每次使同步状态值减 1sync.releaseShared(1);}

通过上面的 API 我们应该能知道其大概的原理了,在 CountDownLatch 初始化的时候会有一个初始的同步状态值,这个同步状态值可以理解为放行前的所要执行的线程数,每次调用 countDown 方法时就把同步状态值减 1,await 方法会自旋检查同步状态值是否为 0,当不为 0 时会阻塞线程,当为 0 时会直接返回,该方法是支持相应 中断的,当线程中断时会抛出异常。因此该方法可以理解为一扇门,只有当指定数量的线程执行完后,才会执行后续的代码。

上面我们已经理解了大概的流程,下面来看下具体的实现代码。

2.2 Sync 同步类

    private static final class Sync extends AbstractQueuedSynchronizer {// 初始化闭锁 count 值Sync(int count) {setState(count);}int getCount() {return getState();}//  通过共享方式尝试获取锁protected int tryAcquireShared(int acquires) {return (getState() == 0) ? 1 : -1;}// 通过共享方式尝试释放锁// 因为该方法是线程共享的,因此需要通过 CAS 操作保证线程安全protected boolean tryReleaseShared(int releases) {for (;;) {int c = getState();// 同步状态值在上一次置 0 时已经放行,因此返回 falseif (c == 0)return false;// 同步状态值 - 1int nextc = c-1;// 为 0 时返回 trueif (compareAndSetState(c, nextc))return nextc == 0;}}}

内部代码很简单,如果你明白了 AQS 的内部原理,这些代码是很容易理解的,如果你对这里的代码感觉到陌生,那么你一定要好好的再去了解下 AQS 了。

AQS 的原理设计的很巧妙,相对来说也比较难理解,后面想梳理这块内容的时候会尝试着总结一下,如果你对这块感兴趣,也可以到我的 GitHub 去看对应的源码。

jdk1.8 源码阅读:https://github.com/zchen96/jdk1.8-source-code-read

参考资料

《Java 并发编程实战》

Java8 CountDownLatch 源码分析相关推荐

  1. Java8 ThreadLocal 源码分析

    可参考文章: Java8 IdentityhashMap 源码分析 IdentityhashMap 与 ThreadLocalMap 一样都是采用线性探测法解决哈希冲突,有兴趣的可以先了解下 Iden ...

  2. Java8 HashMap源码分析

    前言 今天,我们主要来研究一下在Java8中HashMap的数据结构及一些重要方法的具体实现.       研究HashMap的源代码之前,我们首先来研究一下常用的三种数据结构:数组.链表和红黑树. ...

  3. CountDownLatch 源码分析

    1. 类介绍 一个同步辅助类,在完成一组正在其他线程中执行的操作之前,它允许一个或多个线程一直等待.用给定的计数 初始化 CountDownLatch.由于调用了 countDown() 方法,所以在 ...

  4. Java高并发之CountDownLatch源码分析

    概述 CountDownLatch 允许一个或多个线程等待直到在其他线程中执行的一组操作完成的同步辅助.简单来说,就是 CountDownLatch 内部维护了一个计数器,每个线程完成自己的操作之后都 ...

  5. Java8 EnumMap 源码分析

    一.EnumMap 概述 EnumMap 是一个用于存储 key 为枚举类型的 map,底层使用数组实现(K,V 双数组).下面是其继承结构: public class EnumMap<K ex ...

  6. Java8 PriorityBlockingQueue源码分析

    在看这篇总结之前,建议大家先熟悉一下 PriorityQueue,这里主要介绍 PriorityBlockingQueue 一些特殊的性质,关于优先级队列的知识不作着重介绍,因为过程与 Priorit ...

  7. Java8 IdentityHashMap 源码分析

    在讲这个数据结构之前,我们先来看一段代码: public static void main(String[] args) {IdentityHashMap<String, Integer> ...

  8. Java8 ReentrantLock 源码分析

    一.ReentrantLock 概述 1.1 ReentrantLock 简介 故名思义,ReentrantLock 意为可重入锁,那么什么是可重入锁呢?可重入意为一个持有锁的线程可以对资源重复加锁而 ...

  9. Java8 CopyOnWriteArrayList 源码分析

    一.CopyOnWriteArrayList 概述 1.1 概念概述 CopyOnWriteArrayList 是 juc 包下一个线程安全的并发容器,底层使用数组实现.CopyOnWrite 顾名思 ...

最新文章

  1. sqlplus / as sysdba报错ORA-01031: insufficient privileges
  2. 认识Python(day01)
  3. DIP第一章习题解答
  4. GitHub:基于epoll机制的高并发聊天室,c语言实现
  5. antimalware service executable占用内存过高_Win10系统svchost.exe进程占用内存和网速过高的解决方法...
  6. JAVA-抽象类/类继承
  7. layui如何实现添加数据时关闭页面层,并实时刷新表格数据?
  8. 导入表格只有一行 帆软_万万没想到!把x个表格合合合合成一份,10分钟就搞定...
  9. 一款非常好看的雷姆背景的时间单页(附雷姆图片)
  10. 第一章 —— 简单工厂模式(节选自:《大话设计模式》)
  11. 存储管理工具StorageExplorer的基本使用
  12. wsdl2java工具生成客户端代码
  13. 复变函数思维导图梳理
  14. 移动平均法(Moving average,MA) 指数平滑法(Exponential Smoothing,ES)
  15. ascii码01100001_【多选题】计算机中字符a的ASCII码值是(01100001)2,那么字符c的ASCII码值是( )。...
  16. 一文搞懂R(相关系数)与R^2(决定系数)
  17. 【FlashDB】第二步 FlashDB 移植 STM32L475 使用QSPI驱动外部 flash W25Q64之 SFUD 移植
  18. mysql cmd全屏,不能全屏这个问题 ,看完了下面所有解决方法 一个一个试了怎么我是不能全屏呢...
  19. 无线传感网路由协议(一)
  20. 微软开源!世界首个AI量化投资平台 Qlib 基本使用教程

热门文章

  1. shiro之第一个程序认证
  2. thymeleaf模板引擎
  3. shell读取文件到变量、管道重定向、if和while嵌套使用、命令替换
  4. 【过程记录】springboot中使用EhcacheCache+mybatis
  5. 数据结构Java11【图结构概述、图遍历原理(BFS\DFS)、图遍历代码实现】
  6. JavaSE 和 JavaEE 的关系
  7. 【XAduio2】6.如何枚举音频设备
  8. 关系数据库和nosql
  9. Java的容器的线程安全
  10. 记一次@ResponseBody注解不生效问题