并发工具CountDownLatch源码分析
CountDownLatch的作用类似于Thread.join()方法,但比join()更加灵活。它可以等待多个线程(取决于实例化时声明的数量)都达到预期状态或者完成工作以后,通知其他正在等待的线程继续执行。简单的说,Thread.join()是等待具体的一个线程执行完毕,CountDownLatch等待多个线程。
如果需要统计4个文件中的内容行数,可以用4个线程分别执行,然后用一个线程等待统计结果,最后执行数据汇总。这样场景就适合使用CountDownLatch。
本篇从CountDownLatch的源码分析它的原理机制。再给出一个简单的使用案例。
首先认识一下CountDownLatch中的内部类:
- private static final class Sync extends AbstractQueuedSynchronizer {
- private static final long serialVersionUID = 4982264981922014374L;
- Sync(int count) {
- setState(count); // 更新AQS中的state
- }
- int getCount() {
- return getState();
- }
- protected int tryAcquireShared(int acquires) {
- return (getState() == ) ? : -;
- }
- protected boolean tryReleaseShared(int releases) {
- // Decrement count; signal when transition to zero
- for (;;) {
- int c = getState();
- if (c == )
- return false;
- int nextc = c-;
- if (compareAndSetState(c, nextc))
- return nextc == ;
- }
- }
- }
其实CountDownLatch的机制和ReentrantLock有点像,都是利用AQS(AbstractQueuedSynchronizer)来实现的。CountDownLatch的内部类Sync继承AQS,重写了tryAcquireShared()方法和tryReleaseShared()方法。这里的重点是CountDownLatch的构造函数需要传入一个int值count,就是等待的线程数。这个count被Sync用来直接更新为AQS中的state。
1、await()等待方法
- //CountDownLatch
public void await() throws InterruptedException {- sync.acquireSharedInterruptibly();
- }
- //AQS
- public final void acquireSharedInterruptibly(int arg)
- throws InterruptedException {
- if (Thread.interrupted())
- throw new InterruptedException();
- if (tryAcquireShared(arg) < ) // 1
- doAcquireSharedInterruptibly(arg); // 2
- }
- //Sync
- protected int tryAcquireShared(int acquires) {
- return (getState() == ) ? : -;
- }
- //AQS
- private void doAcquireSharedInterruptibly(int arg)
- throws InterruptedException {
- final Node node = addWaiter(Node.SHARED);
- boolean failed = true;
- try {
- for (;;) {
- final Node p = node.predecessor();
- if (p == head) {
- int r = tryAcquireShared(arg);
- if (r >= ) {
- setHeadAndPropagate(node, r);
- p.next = null; // help GC
- failed = false;
- return;
- }
- }
- if (shouldParkAfterFailedAcquire(p, node) &&
- parkAndCheckInterrupt())
- throw new InterruptedException();
- }
- } finally {
- if (failed)
- cancelAcquire(node);
- }
- }
- 调用AQS中的tryAcquireShared()方法时,Sync重写了tryAcquireShared()方法,获取state,判断state是否为0。
- 如果不为0,调用doAcquireSharedInterruptibly()方法,将线程加入队列,挂起线程。
2、countDown()
- public void countDown() {
- sync.releaseShared();
- }
//AQS- public final boolean releaseShared(int arg) {
- if (tryReleaseShared(arg)) {
- doReleaseShared();
- return true;
- }
- return false;
- }
//Sync- protected boolean tryReleaseShared(int releases) {
- // Decrement count; signal when transition to zero
- for (;;) {
- int c = getState();
- if (c == )
- return false;
- int nextc = c-;
- if (compareAndSetState(c, nextc))
- return nextc == ;
- }
- }
重点也是在于Sync重写的tryReleaseShared()方法。利用CAS算法将state减1。如果state减到0,说明所有工作线程都执行完毕,那么就唤醒等待队列中的线程。
使用示例:
- public class CountDownLatchTest {
- private static CountDownLatch countDownLatch = new CountDownLatch();
- private static ThreadPoolExecutor threadPool = new ThreadPoolExecutor(, ,
- 0L, TimeUnit.MILLISECONDS,
- new LinkedBlockingQueue<Runnable>(10));
- public static void main(String[] args) {
- //等待线程
- for (int i = ; i < ; i++) {
- String threadName = "等待线程 " + i;
- threadPool.execute(new Runnable() {
- @Override
- public void run() {
- try {
- System.out.println(threadName + " 正在等待...");
- //等待
- countDownLatch.await();
- System.out.println(threadName + " 结束等待...");
- } catch (InterruptedException e) {
- e.printStackTrace();
- }
- }
- });
- }
- //工作线程
- for (int i = ; i < ; i++) {
- String threadName = "工作线程 " + i;
- threadPool.execute(new Runnable() {
- @Override
- public void run() {
- try {
- System.out.println(threadName + " 进入...");
- //沉睡1秒
- TimeUnit.MILLISECONDS.sleep();
- System.out.println(threadName + " 完成...");
- //通知
- countDownLatch.countDown();
- } catch (InterruptedException e) {
- e.printStackTrace();
- }
- }
- });
- }
- threadPool.shutdown();
- }
- }
执行结果为:
- 等待线程 1 正在等待...
- 等待线程 0 正在等待...
- 工作线程 2 进入...
- 工作线程 3 进入...
- 工作线程 4 进入...
- 工作线程 3 完成...
- 工作线程 2 完成...
- 工作线程 4 完成...
- 等待线程 0 结束等待...
- 等待线程 1 结束等待...
从结果也能看到,等待线程先执行,调用countDownLatch.await()方法开始等待。每个工作线程工作完成以后,都调用countDownLatch.countDown()方法,告知自己的任务完成。countDownLatch初始参数为3,所以3个工作线程都告知自己结束以后,等待线程才开始工作。
并发工具CountDownLatch源码分析的更多相关文章
- 并发工具CyclicBarrier源码分析及应用
本文首发于微信公众号[猿灯塔],转载引用请说明出处 今天呢!灯塔君跟大家讲: 并发工具CyclicBarrier源码分析及应用 一.CyclicBarrier简介 1.简介 CyclicBarri ...
- Java - "JUC" CountDownLatch源码分析
Java多线程系列--“JUC锁”09之 CountDownLatch原理和示例 CountDownLatch简介 CountDownLatch是一个同步辅助类,在完成一组正在其他线程中执行的操作之前 ...
- CountDownLatch 源码分析
CountDownLatch 源码分析: 1:CountDownLatch数据结构 成员变量 Sync类型对象 private final Sync sync; Sync是继承AQS的一个类,Coun ...
- [软件测试]网站压测工具Webbench源码分析
一.我与webbench二三事 Webbench是一个在linux下使用的非常简单的网站压测工具.它使用fork()模拟多个客户端同时访问我们设定的URL,测试网站在压力下工作的性能.Webbench ...
- 网站(Web)压测工具Webbench源码分析
一.我与webbench二三事 Webbench是一个在linux下使用的非常简单的网站压测工具.它使用fork()模拟多个客户端同时访问我们设定的URL,测试网站在压力下工作的性能.Webbench ...
- Java并发——结合CountDownLatch源码、Semaphore源码及ReentrantLock源码来看AQS原理
前言: 如果说J.U.C包下的核心是什么?那我想答案只有一个就是AQS.那么AQS是什么呢?接下来让我们一起揭开AQS的神秘面纱 AQS是什么? AQS是AbstractQueuedSynchroni ...
- JUC之CountDownLatch源码分析
CountDownLatch是AbstractQueuedSynchronizer中共享锁模式的一个的实现,是一个同步工具类,用来协调多个线程之间的同步.CountDownLatch能够使一个或多个线 ...
- bootstrap_栅格系统_响应式工具_源码分析
-----------------------------------------------------------------------------margin 为负 使盒子重叠 等高 等高 ...
- concurrent(五)同步辅助器CountDownLatch & 源码分析
参考文档: https://blog.csdn.net/zxdfc/article/details/52752803 简介 CountDownLatch是一个同步辅助类.允许一个或多个线程等待其他线程 ...
随机推荐
- [luogu]P2279 [HNOI2003]消防局的设立[贪心]
[luogu]P2279 [HNOI2003]消防局的设立 题目描述 2020年,人类在火星上建立了一个庞大的基地群,总共有n个基地.起初为了节约材料,人类只修建了n-1条道路来连接这些基地,并且每两 ...
- luogu P1028 数的计算 x
P1028 数的计算 题目描述 我们要求找出具有下列性质数的个数(包含输入的自然数n): 先输入一个自然数n(n<=1000),然后对此自然数按照如下方法进行处理: 1.不作任何处理; 2.在它 ...
- apache Internal Server Error 解决方法
https://blog.csdn.net/qq_33684377/article/details/78536548 https://blog.csdn.net/LJFPHP/article/deta ...
- [BZOJ3262]:陌上花开(CDQ分治)
题目传送门 题目描述 有$n$朵花,每朵花有三个属性:花形$(s)$.颜色$(c)$.气味$(m)$,用三个整数表示.现在要对每朵花评级,一朵花的级别是它拥有的美丽能超过的花的数量.定义一朵花$A$比 ...
- 浅谈IPv4至IPv6演进的实施路径
作者:个推运维平台网络工程师 宗堂 1 业务背景 在互联网呈现爆炸式发展的今天, IPv4网络地址数量匮乏等问题将会影响到我国的互联网发展与应用,制约物联网.5G等新业务开展.今年4月国家工信部发 ...
- 大数据笔记(十六)——Hive的客户端及自定义函数
一.Hive的Java客户端 JDBC工具类:JDBCUtils.java package demo.jdbc; import java.sql.DriverManager; import java. ...
- Ubuntu启动 卡在checking battery state 解决方案
Ubuntu启动,卡在checking battery statALT + F1或者CTRL+ALT+F6切换到命令行[CTRL+ALT+F7返回界面]执行 sudo gdm start后就可以正常登 ...
- 豆瓣镜像安装python库
- 图书-软件架构:《Design Patterns: Elements of Reusable Object-Oriented Software》(即后述《设计模式》一书)
ylbtech-图书-软件架构:<Design Patterns: Elements of Reusable Object-Oriented Software>(即后述<设计模式&g ...
- 分布式ID生成 - 雪花算法
雪花算法是一种生成分布式全局唯一ID的经典算法,关于雪花算法的解读网上多如牛毛,大多抄来抄去,这里请参考耕耘的小象大神的博客ID生成器,Twitter的雪花算法(Java) 网上的教程一般存在两个问题 ...