AQS系列(五)- CountDownLatch的使用及原理

前言

前面四节学完了AQS最难的两种重入锁应用,下面两节进入实战学习,看看JUC包中其他的工具类是如何运用AQS实现特定功能的。今天一起看一下CountDownLatch。

CountDownLatch可以用来实现多个线程执行完一个功能后让另一个线程继续执行的功能。常见的场景比如大文件的处理,我们需要对一个或多个文件进行处理,处理完之后再统一入库,这时我们就可以用到CountDownLatch了。

一、使用样例

 1 public static void main(String[] args) {
 2         // 指定初始容量
 3         CountDownLatch latch = new CountDownLatch(3);
 4         // 启动三个线程,每个线程独自处理文件
 5         for (int i = 0;i < 3; i++) {
 6             new Thread(() -> {
 7                 System.out.println(Thread.currentThread().getName() + " 正在处理文件");
 8                 try {
 9                     Thread.sleep(2000);
10                 } catch (InterruptedException e) {
11                     e.printStackTrace();
12                 }
13                 System.out.println(Thread.currentThread().getName() + " 处理完毕");
14                 latch.countDown();
15             }).start();
16         }
17         try {
18             latch.await();
19         } catch (Exception e) {
20             e.printStackTrace();
21         }
22         System.out.println("所有文件处理完成后,统一入库");
23     }

运行结果:

1 Thread-0 正在处理文件
2 Thread-2 正在处理文件
3 Thread-1 正在处理文件
4 Thread-0 处理完毕
5 Thread-2 处理完毕
6 Thread-1 处理完毕
7 所有文件处理完成后,统一入库

效果就是这样,下面我们一起看看它是如何实现的这种功能。

二、源码学习

1、首先我们看看new CountDownLatch(3) 做了什么事情

1 public CountDownLatch(int count) {
2         if (count < 0) throw new IllegalArgumentException("count < 0");
3         this.sync = new Sync(count);
4     }

继续追踪可以发现,就是将count赋值给AQS中的成员变量state,表示已经有3个线程占用了锁。

2、看countDown()方法做了什么

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

可以看到,countDown走的是释放共享锁的逻辑,从给state赋值也可以猜到用的是共享锁-有多个线程且state可赋大于0的值。继续看releaseShared逻辑:

1 public final boolean releaseShared(int arg) {
2         if (tryReleaseShared(arg)) {
3             doReleaseShared();
4             return true;
5         }
6         return false;
7     }

可以看到就是读锁释放的逻辑,其中doReleaseShared方法实现逻辑相同就不看了,不同的是tryReleaseShared方法,下面跟进:

 1 protected boolean tryReleaseShared(int releases) {
 2             // Decrement count; signal when transition to zero
 3             for (;;) {
 4                 int c = getState();
 5                 if (c == 0)
 6                     return false;
 7                 int nextc = c-1;
 8                 if (compareAndSetState(c, nextc))
 9                     return nextc == 0;
10             }
11         }

此方法在CountDownLatch中的内部类Sync中得到实现,逻辑为将state-1,并且如果是0的话返回true。返回true后在releaseShared方法中会进入if里面,走唤醒后续节点的逻辑doReleaseShared方法,在该方法中唤醒的main线程。main线程什么时候被挂起的?且看下面。

3、await方法

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

await调用了可响应中断的获取共享锁方法,继续查看:

1 public final void acquireSharedInterruptibly(int arg)
2             throws InterruptedException {
3         if (Thread.interrupted())
4             throw new InterruptedException();
5         if (tryAcquireShared(arg) < 0)
6             doAcquireSharedInterruptibly(arg);
7     }

此方法是AQS中的公用模板方法,不同点在于各实现类的实现逻辑,在CountDownLatch中对tryAcquireShared方法进行了实现,实现逻辑如下:

1 protected int tryAcquireShared(int acquires) {
2             return (getState() == 0) ? 1 : -1;
3         }

即如果state==0则能获取到锁,否则获取不到。获取不到进入下面的doAcquireSharedInterruptibly方法,最终会将head的waitStatus设置为-1,自己挂起等待唤醒。

三、总结

CountDownLatch是基于共享锁实现的并发控制功能,现在对总的实现逻辑做个梳理:首先在构造器初始化CountDownLatch的时候,就会给AQS中的state赋值,表示共享锁已经被获取了N次;然后每执行一次countDown则共享锁释放一次,直到释放完;await方法是加锁的逻辑,但加锁条件是state==0时才会加锁成功,否则挂起;最后,当通过countDown的调用将state减为0后,会唤醒处于阻塞状态的主线程,让其 获取到锁并执行。

原文地址:https://www.cnblogs.com/zzq6032010/p/12076689.html

时间: 2024-11-05 15:45:09

AQS系列(五)- CountDownLatch的使用及原理的相关文章

React-Native系列Android——Native与Javascript通信原理(三)

前面两篇博客,详细分析了Native与Javascript通信的过程,可以满足绝大部分场景下Native和Javascript的相互调用,但是仍然有不健全的情况. 比如Javascript层要实时获取Native的一些状态,就需要Native被动地向Javascript层通信了.这个过程区别于通信第一篇中Native主动向Javascript层通信,本篇博客就来研究下这样一个被动回调的过程! 在阅读本篇博客前,希望能回顾下前两篇. React-Native系列Android--Native与Ja

So Easy! Oracle在Linux上的安装配置系列五

So Easy! Oracle在Linux上的安装配置系列五 本篇是监听器的配置的续篇,上一小节我们创建了一个监听器,创建了密码文,在监听和实例都启动的情况下,从远程windows即时客户端连接到了oracle服务器.本篇我将继续说监听器,还将完成相关的实验 以下内容整理自网络 Oracle 监听器 Listener 是一个重要的数据库服务器组件,在整个 Oracle 体系结构中,扮演着重要的作用.它负责管理 Oracle 数据库和客户端之间的通讯,它在一个特定的网卡端口(默认是TCP 1521

AQS系列(一)- ReentrantLock的加锁

前言 AQS即AbstractQueuedSynchronizer,是JUC包中的一个核心抽象类,JUC包中的绝大多数功能都是直接或间接通过它来实现的.本文是AQS系列的第一篇,后面会持续更新多篇,争取将JUC包中AQS相关的常用功能讲清楚,一方面巩固自己的知识体系,一方面亦可与各位园友互相学习.寒冷的冬天,要用技术来温暖自己. 一.AQS与ReentrantLock的关系 先奉上一张自制的丑陋类图 从下往上看,ReentrantLock类内部有两个静态内部类FairSync和NonfairSy

AQS系列(三)- ReentrantReadWriteLock读写锁的加锁

前言 前两篇我们讲述了ReentrantLock的加锁释放锁过程,相对而言比较简单,本篇进入深水区,看看ReentrantReadWriteLock-读写锁的加锁过程是如何实现的,继续拜读老Lea凌厉的代码风. 一.读写锁的类图 读锁就是共享锁,而写锁是独占锁.读锁与写锁之间的互斥关系为:读读可同时执行(有条件的):读写与写写均互斥执行.注意此处读读可并行我用了有条件的并行,后文会对此做介绍. 继续奉上一张丑陋的类图: 可以看到ReentrantReadWriteLock维护了五个内部类,Ree

Apache Kafka系列(五) Kafka Connect及FileConnector示例

Apache Kafka系列(一) 起步 Apache Kafka系列(二) 命令行工具(CLI) Apache Kafka系列(三) Java API使用 Apache Kafka系列(四) 多线程Consumer方案 Apache Kafka系列(五) Kafka Connect及FileConnector示例 一. Kafka Connect简介 Kafka是一个使用越来越广的消息系统,尤其是在大数据开发中(实时数据处理和分析).为何集成其他系统和解耦应用,经常使用Producer来发送消

Spring Boot干货系列:(三)启动原理解析

Spring Boot干货系列:(三)启动原理解析 2017-03-13 嘟嘟MD 嘟爷java超神学堂 前言 前面几章我们见识了SpringBoot为我们做的自动配置,确实方便快捷,但是对于新手来说,如果不大懂SpringBoot内部启动原理,以后难免会吃亏.所以这次博主就跟你们一起一步步揭开SpringBoot的神秘面纱,让它不在神秘. 正文 我们开发任何一个Spring Boot项目,都会用到如下的启动类 从上面代码可以看出,Annotation定义(@SpringBootApplicat

RX系列五 | Schedulers线程控制

RX系列五 | Schedulers线程控制 在我们上一篇文章中的,我们的小例子里有这么一段代码 //网络访问 .observeOn(Schedulers.io()) 事实上,我们在使用网络操作的时候,便可以控制其运行在哪个线程中,而Schedulers类,有四个方法,分别是 Schedulers.immediate(); Schedulers.newthread(); Schedulers.io(); Schedulers.computation(); 以及RxAndroid中的Android

MyBatis 系列五 之 关联映射

MyBatis 系列五 之 关联映射 一对多的关联映射 一对多关联查询多表数据 1.1在MyBatis映射文件中做如下配置 <!--一对多单向的连接两表的查询--> <resultMap type="Dept" id="deptMapper"> <id property="deptNo" column="deptNo"/> <result property="deptName

C语言快速入门系列(五)

C语言快速入门系列(五) C语言指针初涉                                           ------转载请注明出处:coder-pig 本节引言: 上一节我们对C语言复合数据类型中的数组进行了解析,在本节中,我们会对C语言复合数据类型中的 重点,C语言的灵魂-----指针进行学习!使用指针的好处:利用指针可以表示与使用复杂的数据结构; 更加方便地使用我们的数组与字符串;可以像汇编语言一样直接处理内存单元地址;可以动态地进行内存空间 分配,C语言指针是重点,同

互联网金融的前世、今生和未来-系列五(今生):互联网金融的有效监管

互联网金融的前世.今生和未来--系列一:山雨欲来 互联网金融的前世.今生和未来-系列二(前世):金融与技术的首次亲密接触之金融电子化 互联网金融的前世.今生和未来-系列三(今生):一场跨界的战争 互联网金融的前世.今生和未来-系列四(今生):百花齐放的互联网金融业态 今生:金融与互联网的深度融合--互联网金融 金融行业作为现代经济的核心,对国民经济的平稳运行至关重要.为防止出现金融市场失灵的情况,如内幕交易.信息不对称.信托责任.监管套利.系统性风险及羊群效应等,世界各国政府普遍会基于本国的金融