1 前言

CountDownLatch是一种同步辅助工具类,它允许一个或多个线程等待,直到在其他线程中执行的一组操作完成为止。(源码分析基于JDK1.8)
CountDownLatch需要用给定的闩锁计数count初始化。await方法使当前线程阻塞(每执行一次countDown方法就将闩锁计数减1),直到闩锁计数达到零时(所有因此阻塞等待的线程都)才会被唤醒。CountDownLatch是一次性使用的同步工具,闩锁计数无法重置,如果需要重置计数,可能使用CyclicBarrier更合适。

2 用法示例

1) 示例1

我们需要解析一个Excel里多个sheet的数据,此时可以考虑使用多线程,每个线程解析一个sheet里的数据,等到所有的sheet都解析完之后,程序需要提示解析完成。在这个需求中,要实现主线程等待所有线程完成sheet的解析操作,最简单的做法是使用join()方法.

public class JoinCountDownLatchTest {public static void main(String[] args) throws InterruptedException {Thread parser1 = new Thread(new Runnable() {@Overridepublic void run() {System.out.println("parser1 finish");}});Thread parser2 = new Thread(new Runnable() {@Overridepublic void run() {System.out.println("parser2 finish");}});parser1.start();parser2.start();parser1.join();parser2.join();System.out.println("all parser finish");}
}

CountDownLatch可以实现join类似的功能,但它更强大,它提供了很多API方法,能够实现更精准的控制。

CountDownLatch的构造方法必须传入一个int类型的参数,这个参数作为闩锁的计数器。

CountDownLatch的countDownawait方法一般都要配合使用。await方法(休眠)阻塞当前线程,而每调用一次countDown方法,闩锁计数就减1,当其减为0时,当前线程就被唤醒、await方法得以返回。

 class CountDownLatchTest {static CountDownLatch c = new CountDownLatch(2);public static void main(String[] args) throws InterruptedException {Thread parser1 = new Thread(() -> {System.out.println("parser1 finish");c.countDown();});Thread parser2 = new Thread(new Runnable() {@Overridepublic void run() {System.out.println("parser2 finish");c.countDown();}});parser1.start();parser2.start();c.await();System.out.println("all parser finish");}
}

打印结果:

parser1 finish
parser2 finish
all parser finish

c.await();被注释掉,就不能保证打印的先后顺序,输出结果如下:

all parser finish
parser1 finish
parser2 finish

2) 示例2

这里有两个类Driver和Worker,分别表示驱动者、工作者线程。这里使用了两个CountDownLatch对象,第一个表示启动信号,可防止任何工作者线程Worker前进处理,直到驱动者Driver为它们做好准备为止;第二个表示完成信号,允许驱动者Driver等到所有工作者线程Worker都完成任务为止。

class Driver { // ...void main() throws InterruptedException {CountDownLatch startSignal = new CountDownLatch(1);CountDownLatch doneSignal = new CountDownLatch(N);for (int i = 0; i < N; ++i) // create and start threadsnew Thread(new Worker(startSignal, doneSignal)).start();doSomethingElse();            // don't let run yet 做准备startSignal.countDown();      // let all threads proceed doSomethingElse();doneSignal.await();           // wait for all to finish}
}class Worker implements Runnable {private final CountDownLatch startSignal;private final CountDownLatch doneSignal;Worker(CountDownLatch startSignal, CountDownLatch doneSignal) {this.startSignal = startSignal;this.doneSignal = doneSignal;}public void run() {try {startSignal.await();doWork();doneSignal.countDown();} catch (InterruptedException ex) {} // return;}void doWork() { ... }
}

3 实现分析

CountDownLatch的实现主要基于同步器AbstractQueuedSynchronizer,它利用AQS实现了一个共享锁. CountDownLatch主要有一个Sync类型成员变量sync, Sync是继承抽象类AbstractQueuedSynchronizer的静态内部类。

private final Sync sync;

1) 构造方法CountDownLatch(int)

CountDownLatch的构造方法主要是执行this.sync = new Sync(count)对sync进行实例化, 而Sync(int)又将父类AbstractQueuedSynchronizer的实例变量state设置为指定的count。

public CountDownLatch(int count) {if (count < 0) throw new IllegalArgumentException("count < 0");this.sync = new Sync(count);//实例化
}Sync(int count) {setState(count);//将`AbstractQueuedSynchronizer`的state设为count}

2) 静态内部类Sync

Sync主要重写了父类的tryAcquireSharedtryReleaseShared方法,这两个方法都是实现共享锁所必须重写的相关方法,其作用分别是尝试获取共享状态、尝试释放共享状态,两者刚好配对。有关AQS详细分析,请看之前的博客AbstractQueuedSynchronizer实现原理分析。

protected int tryAcquireShared(int acquires) {//state为0,锁闩计数为0 ,返回1,获取共享状态成功//反之锁闩计数不为0,返回-1,获取共享状态失败。return (getState() == 0) ? 1 : -1;
}protected boolean tryReleaseShared(int releases) {// Decrement count; signal when transition to zerofor (;;) {int c = getState();if (c == 0)  //state已经为0,非法状态返回false.(只有在已获取锁,即state非零时,才有释放锁的说法)return false;int nextc = c-1;if (compareAndSetState(c, nextc))//cas自旋将state减1return nextc == 0;//state自减1后为零,返回true,可释放锁。反之返回false,还不能释放锁。}
}

另外Sync还提供了一个方法getCount,返回当前剩余的闩锁计数,它直接调用父类AQS的getState实现。

int getCount() {return getState();
}

3) await

await使当前线程休眠等待,直到count减少至0或线程中断。

await调用了AQS的acquireSharedInterruptibly方法,acquireSharedInterruptibly获取共享锁并响应中断。

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

await(long , TimeUnit )是await()的超时版本。

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

4) countDown

countDown将闩锁计数递减1,若递减后为0就将唤醒所有阻塞等待的线程。如果闩锁的计数(递减前)已经为零,就啥也不做,恰好与上面tryReleaseShared方法体中的if (c == 0) return false;所对应。

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

5) getCount

getCount用于查询当前的闩锁计数

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

倒数闩锁CountDownLatch源码浅析相关推荐

  1. Android Loader机制全面详解及源码浅析

    原文出处:csdn@工匠若水,http://blog.csdn.net/yanbober/article/details/48861457 一.概述 在Android中任何耗时的操作都不能放在UI主线 ...

  2. latch.await java有什么作用_java相关:CountDownLatch源码解析之await()

    java相关:CountDownLatch源码解析之await() 发布于 2020-6-18| 复制链接 摘记: CountDownLatch 源码解析-- await(),具体内容如下上一篇文章说 ...

  3. fetch first mysql_MySQL多版本并发控制机制(MVCC)源码浅析

    MySQL多版本并发控制机制(MVCC)-源码浅析 前言 作为一个数据库爱好者,自己动手写过简单的SQL解析器以及存储引擎,但感觉还是不够过瘾.<>诚然讲的非常透彻,但只能提纲挈领,不能让 ...

  4. 【flink】Flink 1.12.2 源码浅析 : Task数据输入

    1.概述 转载:Flink 1.12.2 源码浅析 : Task数据输入 在 Task 中,InputGate 是对输入的封装,InputGate 是和 JobGraph 中 JobEdge 一一对应 ...

  5. 【flink】Flink 1.12.2 源码浅析 :Task数据输出

    1.概述 转载:Flink 1.12.2 源码浅析 :Task数据输出 Stream的计算模型采用的是PUSH模式, 上游主动向下游推送数据, 上下游之间采用生产者-消费者模式, 下游收到数据触发计算 ...

  6. 【flink】Flink 1.12.2 源码浅析 : StreamTask 浅析

    1.概述 转载:Flink 1.12.2 源码浅析 : StreamTask 浅析 在Task类的doRun方法中, 首先会构建一个运行环境变量RuntimeEnvironment . 然后会调用lo ...

  7. 【flink】Flink 1.12.2 源码浅析 : Task 浅析

    1.概述 转载:Flink 1.12.2 源码浅析 : Task 浅析 Task 表示TaskManager上并行 subtask 的一次执行. Task封装了一个Flink operator(也可能 ...

  8. hive 强转为string_String 源码浅析————终结篇

    写在前面 说说这几天看源码的感受吧,其实 jdk 中的源码设计是最值得进阶学习的地方.我们在对 api 较为熟悉之后,完全可以去尝试阅读一些 jdk 源码,打开 jdk 源码后,如果你英文能力稍微过得 ...

  9. hashmap允许null键和值吗_hashMap底层源码浅析

    来源:https://blog.csdn.net/qq_35824590/article/details/111769203 hashmap是我们经常使用的一个工具类.那么知道它的一些原理和特性吗? ...

最新文章

  1. Elasticsearch——并发冲突以及解决方案
  2. uniapp中分包及分包优化
  3. 如何写出一个好的单例模式
  4. 海思芯片对比选型_海思芯片的选型及特征参考说明大全
  5. 微服务探索与实践—总述
  6. Linux相关图解随记
  7. Taro多端开发实现原理与项目实战(一)
  8. Virtuoso崩掉时layout数据恢复
  9. 干货下载丨开源数据库安全管理
  10. Head First设计模式读书笔记六 第七章下 外观模式
  11. shell基础之编译安装nginx
  12. Must Know Tips/Tricks in Deep Neural Networks (by Xiu-Shen Wei)
  13. 若依框架入门(前后端分离版本)
  14. mysql反思范文_MySQL学习笔记(一)
  15. 图灵机跟现实电子计算机哪个计算能力强,计算机不是只会“计算”,图灵机也不是一台“机器”|AI那厮...
  16. 一台pc计算机系统启动不了,电脑装系统引导不进去怎么办
  17. 游承超:钢化玻璃膜既保护屏幕又不影响触感(4P)
  18. 小米12S和红米K50至尊版哪个好
  19. java毕业设计在线音乐系统Mybatis+系统+数据库+调试部署
  20. App Links的使用以及坑

热门文章

  1. 历年阿里面试题汇总(2017年不断更新中)
  2. 19款Windows实用软件推荐,满满的干货,总有一款是你必备的
  3. 如何用Python进行数据分析
  4. java excel 筛选_Java 在Excel中设置筛选器
  5. 解决macOS无法在线升级更新的问题
  6. python实践输出星星_打印星星 - Python
  7. 主板中的电池是怎样放电的?
  8. 经典4电阻差动放大器
  9. 图片懒加载和Vue路由懒加载
  10. 【Java】23 函数式编程