CountDownLatch的countDown()方法的底层源码
一、CountDownLatch的构造方法
// 创建倒数闩,设置倒数的总数State的值
CountDownLatch doneSignal = new CountDownLatch(N);
二、countDown() 方法的作用
countDown() 方法的主要作用是将 CountDownLatch 的内部计数器减一。如果计数器减到零,则会唤醒所有等待的线程
三、countDown() 方法的源码分析
1、线程进入 countDown() 完成计数器减一(释放锁)的操作
public void countDown() {
sync.releaseShared(1);
}
public final boolean releaseShared(int arg) {
// 尝试释放共享锁
if (tryReleaseShared(arg)) {
// 释放锁成功则开始唤醒阻塞节点
doReleaseShared();
return true;
}
return false;
}
2、更新 state 值,每调用一次,state 值减1,当 state -1 正好为 0 时,返回 true
protected boolean tryReleaseShared(int releases) {
for (;;) {
int c = getState();
// 条件成立说明前面【已经有线程触发唤醒操作了,不需要再次触发】,这里返回 false
if (c == 0)
return false;
// 计数器减一
int nextc = c-1;
if (compareAndSetState(c, nextc))
// 计数器为 0 时返回 true
return nextc == 0;
}
}
i、获取当前状态:调用 getState() 获取当前计数器的值。
ii、检查计数器是否为零:如果计数器已经为零,则返回 false,表示说明前面已经有线程触发唤醒操作了,不需要再次触发。
iii、减少计数器:将计数器减一,得到新的计数器值 nextc。
vi、CAS 操作:使用 compareAndSetState(c, nextc) 尝试将计数器的值从 c 更新为 nextc。如果成功,则返回 nextc == 0,表示计数器是否减到零
3、当state = 0 时,当前线程需要执行唤醒阻塞节点的任务
private void doReleaseShared() {
for (;;) {
Node h = head;
// 判断队列是否是空队列
if (h != null && h != tail) {
int ws = h.waitStatus;
// 头节点的状态为 signal,说明后继节点需要被唤醒过
if (ws == Node.SIGNAL) {
// cas 设置头节点的状态为 0,设置失败继续自旋
if (!compareAndSetWaitStatus(h, Node.SIGNAL, 0))
continue;
// 唤醒后继节点
unparkSuccessor(h);
}
// 如果有其他线程已经设置了头节点的状态,重新设置为 PROPAGATE 传播属性
else if (ws == 0 && !compareAndSetWaitStatus(h, 0, Node.PROPAGATE))
continue;
}
// 条件不成立说明被唤醒的节点非常积极,直接将自己设置为了新的head,
// 此时唤醒它的节点(前驱)执行 h == head 不成立,所以不会跳出循环,会继续唤醒新的 head 节点的后继节点
if (h == head)
break;
}
}
i、检查头节点:获取等待队列的头节点 h
ii、检查头节点的状态:如果头节点的状态为 Node.SIGNAL,则表示需要唤醒后续节点。
iii、CAS 操作:使用 compareAndSetWaitStatus(h, Node.SIGNAL, 0) 尝试将头节点的状态从 Node.SIGNAL 更新为 0。如果成功,则调用 unparkSuccessor(h) 唤醒后续节点。
vi、检查头节点是否变化:如果头节点没有变化,则退出循环。
四、unparkSuccessor()方法的源码分析
unparkSuccessor() 是 Java 并发工具包(JUC)中 AbstractQueuedSynchronizer (AQS) 框架的核心方法之一。它的作用是唤醒等待队列中某个节点的后继节点(即下一个等待的线程)。
这个方法通常在线程释放锁或条件满足时调用,用于唤醒等待的线程
private void unparkSuccessor(Node node) {
// 获取当前节点的等待状态
int ws = node.waitStatus;
if (ws < 0) // 如果状态为 SIGNAL 或 CONDITION,尝试将其重置为 0
compareAndSetWaitStatus(node, ws, 0);
// 获取当前节点的后继节点
Node s = node.next;
if (s == null || s.waitStatus > 0) { // 如果后继节点为空或已被取消
s = null;
// 从队列尾部向前遍历,找到最接近的有效节点
for (Node t = tail; t != null && t != node; t = t.prev)
if (t.waitStatus <= 0) // 找到未被取消的节点
s = t;
}
if (s != null) // 如果找到有效节点,唤醒对应的线程
LockSupport.unpark(s.thread);
}
源码逐行解析
1、获取当前节点的等待状态:

获取传入节点 node 的 waitStatus 状态。waitStatus 是 AQS 中 Node 类的一个字段,表示节点的状态,可能的值包括:
CANCELLED (1):节点已被取消
SIGNAL (-1):节点需要唤醒其后继节点
CONDITION (-2):节点在条件队列中等待
PROPAGATE (-3):共享模式下需要传播唤醒操作
2、重置节点的等待状态:

如果当前节点的 waitStatus 是负数(通常是 SIGNAL 或 CONDITION),则尝试通过 CAS 操作将其重置为 0。这一步是为了确保节点状态被正确清理
3、获取当前节点的后继节点:

获取当前节点的直接后继节点 s
4、获取当前节点的后继节点:

如果后继节点 s 为空或已被取消(waitStatus > 0),则需要从队列尾部向前遍历,找到最接近的有效节点。这是因为 AQS 的队列是一个双向链表,可能存在并发修改的情况,导致直接后继节点不可用。
从队列尾部 tail 开始向前遍历
找到第一个 waitStatus <= 0 的节点(即未被取消的节点)
将该节点赋值给 s
5、唤醒有效节点的线程:

如果找到有效节点 s,则调用 LockSupport.unpark(s.thread) 唤醒该节点对应的线程
CountDownLatch的countDown()方法的底层源码的更多相关文章
- Android开发之漫漫长途 Ⅵ——图解Android事件分发机制(深入底层源码)
该文章是一个系列文章,是本人在Android开发的漫漫长途上的一点感想和记录,我会尽量按照先易后难的顺序进行编写该系列.该系列引用了<Android开发艺术探索>以及<深入理解And ...
- 为什么很多类甚者底层源码要implements Serializable ?
为什么很多类甚者底层源码要implements Serializable ? 在碰到异常类RuntimeException时,发现Throwable实现了 Serializable,还有我们平进的ja ...
- List-LinkedList、set集合基础增强底层源码分析
List-LinkedList 作者 : Stanley 罗昊 [转载请注明出处和署名,谢谢!] 继上一章继续讲解,上章内容: List-ArreyLlist集合基础增强底层源码分析:https:// ...
- 从底层源码浅析Mybatis的SqlSessionFactory初始化过程
目录 搭建源码环境 POM依赖 测试SQL Mybatis全局配置文件 UserMapper接口 UserMapper配置 User实体 Main方法 快速进入Debug跟踪 源码分析准备 源码分析 ...
- Java泛型底层源码解析-ArrayList,LinkedList,HashSet和HashMap
声明:以下源代码使用的都是基于JDK1.8_112版本 1. ArrayList源码解析 <1. 集合中存放的依然是对象的引用而不是对象本身,且无法放置原生数据类型,我们需要使用原生数据类型的包 ...
- 2018.11.20 Struts2中对结果处理方式分析&struts2内置的方式底层源码剖析
介绍一下struts2内置帮我们封装好的处理结果方式也就是底层源码分析 这是我们的jar包里面找的位置目录 打开往下拉看到result-type节点 name那一列就是我们的type类型取值 上一篇博 ...
- 总结HashSet以及分析部分底层源码
总结HashSet以及分析部分底层源码 1. HashSet继承的抽象类和实现的接口 继承的抽象类:AbstractSet 实现了Set接口 实现了Cloneable接口 实现了Serializabl ...
- LInkedList总结及部分底层源码分析
LInkedList总结及部分底层源码分析 1. LinkedList的实现与继承关系 继承:AbstractSequentialList 抽象类 实现:List 接口 实现:Deque 接口 实现: ...
- Vector总结及部分底层源码分析
Vector总结及部分底层源码分析 1. Vector继承的抽象类和实现的接口 Vector类实现的接口 List接口:里面定义了List集合的基本接口,Vector进行了实现 RandomAcces ...
- 深入理解Whitelabel Error Page底层源码
深入理解Whitelabel Error Page底层源码 (一)服务器请求处理错误则转发请求url StandardHostValve的invoke()方法将根据请求的url选择正确的Context ...
随机推荐
- mxGraph绘制机构图
简单介绍一下使用的依赖: JGraphX package JGraphX is a Java Swing diagramming (graph visualisation) library lic ...
- react报错Can't resolve 'react' in 'E:\reactweb\preact\my-app\node_modules\react-dom\cjs'
执行如下: npm install -g react npm install react --save 类似这种依赖项(react,react-dom 等)报错,哪个报错执行哪个即可 执行上述两句就 ...
- java中的ArrayBlockingQueue
ArrayBlockingQueue ArrayBlockingQueue 是 Java 并发包 (java.util.concurrent) 中的一个线程安全的阻塞队列实现. 它基于数组实现,容量固 ...
- 打造有效安全闭环,天翼云MDR来了!
随着网络攻-防对抗形势愈演愈烈,传统的安全防护模式已难以应对频率暴增.昼夜不停的网络安全攻-击,提升组织安全防护能力势在必行.事实上,一些单位在网络安全建设工作中经验不足,在安全组件/设备采购方面大量 ...
- Pyinstaller打包工具
本篇博客主要介绍的是pyinstaller在windows下的基本使用和基础避坑 在windows中使用pyinstaller工具打包时会出现一个问题,在打包列表会看到这样的警告信息: django. ...
- 再次使用layui遇见问题
Layui似乎只接收data里的数据,所以只能使用这个方式把原有数据放入dataparseData: function (res) { //res 即为原始返回的数据 return { "c ...
- MySql执行Sql语句时出现“MySqlException: Parameter ‘@maxNo‘ must be defined.”的错误
1.问题描述 具体执行的SQL和报错的信息如下图所示: 2.解决办法 连接字符串中增加Allow User Variables=True;的配置即可,如下所示: Server=192.168.2.1; ...
- Linux 之 vi / vim
vi / vim Vim 是从 vi 发展出来的一个文本编辑器.代码补全.编译及错误跳转等方便编程的功能特别丰富,在程序员中被广泛使用. 简单的来说, vi 是老式的字处理器,不过功能已经很齐全了 ...
- .NET适配HarmonyOS进展
1. 前言 目前国产化系统浪潮下,适配鸿蒙是中国软件大势所趋,.NET作为最适合开发客户端语言之一,适配鸿蒙系统(HarmonyOS Next)是目前.NET开发者最关心的事情.我目前业余时间正在移植 ...
- 同步工具-SeaTunnel使用
一.介绍 SeaTunnel 是一个非常好用.超高性能的分布式数据集成平台,架构于 Apache Spark 和 Apache Flink 之上,实现了海量数据的实时同步与转换.每天可以稳定高效地同步 ...