详解CountDownLatch底层源码
大家好,我是此林。
今天来分享一下CountDownLatch的底层源码。
CountDownLatch 是 Java 并发包 (java.util.concurrent) 中的线程之间同步工具类,主要用于协调多个线程的执行顺序。其核心思想是通过计数器实现线程间的"等待-唤醒"机制,也就是AQS那一套。
话不多说,先上一个例子。
@Slf4j
public class A {public static void main(String[] args) throws InterruptedException {CountDownLatch latch = new CountDownLatch(1);for (int i = 0; i < 5; i++) {new Thread(() -> {try {latch.await();log.info(Thread.currentThread().getName() + ":启动");} catch (InterruptedException e) {throw new RuntimeException(e);}}, "线程-"+i).start();}log.info("准备中...");Thread.sleep(5000);latch.countDown();}
}
这段代码里,5个线程需要等待主线程统一发号施令,然后才会启动执行。
可以看到,主线程休眠5秒后,发号施令,5个线程才各自启动。
1. CountDownLatch的源码
接下来,进入正题,来看下CountDownLatch的源码。
怎么看源码呢?一说到看源码,有些朋友可能觉得源码冗长,眼花缭乱的,下面说下具体方法。
1.1. 先看整体结构。
(快捷键:Alt + 7)
可以看到,CountDownLatch 里还有个 Sync 静态内部类,这个类继承了 AQS,重写了 tryAccquireShared() 和 tryReleaseShared() 方法。
其他方法很常见了吧,之前案例里就用过。
1. CountDownLatch(int n) :构造方法,传入计数器。
2. await() :线程阻塞等待到 CountDownLatch 减到 0。
3. latch.await(1, TimeUnit.SECONDS):线程最多阻塞等待1秒钟,1秒钟过后无论CountDownLatch 是否减到 0,都开始执行。
4. countDown():计数器的值减1。
1.2. 接下来,我们去看各个方法的源码。
1. CountDownLatch(int n)构造方法
CountDownLatch latch = new CountDownLatch(1);
实际上,CountDownLatch构造方法里会实例化静态内部类 Sync。
这个 setState() 方法就是父类AQS里的方法,设置 state 属性。
我们都知道 AQS 里锁的状态、重入等等都是由这个 state 公共变量来维护的。
为了保证多线程环境下内存的可见性,state 变量用了 volatile 关键字修饰。
2. countDown() 方法
具体会执行到这个方法:
这个无非是无限for循环,不断CAS自旋,直到成功、原子性地把state减1。
减一之后如果发现state等于0了,那么就返回true,接下来会去唤醒阻塞队列的线程。
这里会去唤醒阻塞队列的第一个线程,即头结点的后继节点。
问:为什么是头节点的后继?
答:AQS 队列设计,头节点是虚拟节点(不关联线程),仅作为占位符。head (dummy) → Node1 (thread1) → Node2 (thread2) → ...
再问:那为什么只是唤醒第一个线程?不应该唤醒所有线程吗?
答:第一个线程被唤醒了之后,它最后会执行AQS的acquire()方法。
如果是共享模式下,会额外执行这一步,它会自动唤醒下一个线程,也就是会不断链式唤醒,相当于唤醒所有线程了。
3. 再来看 await() 方法
await() 的作用是让当前线程阻塞等待。
如果执行 countdown() 方法发现 CountDownLatch 减到 0了,那就会唤醒阻塞等待的线程。
latch.await();
这个 acquireSharedInterruptibly() 是AQS里的 模板方法 。
这个 acquireSharedInterruptibly() 也很简单,就一个 if 条件判断,如果为 true,就抛异常。
第一个条件:
Thread.interrupted()
如果线程当前已经是中断状态,那直接抛异常,没必要阻塞等待下去了。
第二个条件:
tryAcquireShared(arg) < 0 &&acquire(null, arg, true, true, false, 0L) < 0)
这个 tryAcquireShared(arg) 实际执行的就是 Sync 里重写 的方法,之前我们类的结构图里也看过。tryAcquireShared(arg) 特别简单,就一行代码。
如果 state 计数器为0了,返回1,state计数器不为0,返回-1。
很明显,
tryAcquireShared(arg) < 0
代表 state 计数器不为0,就说明当前线程要阻塞等待了,继续进入 acquire() 方法,加入阻塞队列。
这个 acquire() 方法就是 AQS 的模板方法,它是AQS同步机制的核心方法,实现了独占/共享模式的资源获取逻辑,支持可中断和超时特性。ReentrantLock/Semaphore等同步器的底层都依赖该方法。
3. AQS 的 acquire() 方法
acquire() 方法没啥花头,就是把当前线程通过不断 CAS 自旋直到成功加入阻塞队列。
整体结构上是个 for 无限循环。
流程图如下:
每次for循环,都会执行以下步骤:
1. 前驱节点有效性检查(无需太关注,会自动清理已取消的前驱节点)
2. 如果当前线程是阻塞队列第一个节点,那就去看下state计数器是否已经减到0了。如果减到0了,当前线程出队,调用 unpark() 方法唤醒下一个线程节点。
3. CAS尝试原子性插入阻塞队列尾部。
这个就是调用 park() 阻塞线程,如果传入了超时时间(执行 latch.await(int n, Timeunit unit),就调用 parkNanos()。
2. 总结一下
总结一下,CountDownLatch 本身就是通过 AQS 的 state 公共变量维护一个计数器。
调用 await() 方法,就是把当前线程加入到阻塞队列,直到 state 变量为 0。
调用 countdown() 方法,就是对 state 变量减一,如果减到0了,那么就唤醒阻塞队列的所有线程。
CountDownLatch也有一些缺点,比如:
它只能一次性使用,无法重置计数。如果需要多次使用 CountDownLatch,必须新建实例,不如 CyclicBarrier 可以复用。
今天的分享就到这里了。
关注我吧,我是此林,带你看不一样的世界!
相关文章:
详解CountDownLatch底层源码
大家好,我是此林。 今天来分享一下CountDownLatch的底层源码。 CountDownLatch 是 Java 并发包 (java.util.concurrent) 中的线程之间同步工具类,主要用于协调多个线程的执行顺序。其核心思想是通过计数器实现线程间的"等待-唤醒"机制&#…...
Python每日一题(9)
Python每日一题 2025.3.29 一、题目二、分析三、源代码四、deepseek答案五、源代码与ai分析 一、题目 question["""企业发放的奖金根据利润提成。利润(I)低于或等于10万元时,奖金可提10%,利润高于10万元,低于20万元时,低于10万元的部分按10%提成,高于10万元的部…...
一阶谓词逻辑表示法、产生式表示法、框架表示法深度对比
前文我们已经深度学习了一阶谓词逻辑表示法、产生式表示法和框架表示法这三种知识表示方法,那么它们之间有什么异同点呢?接下来我们对它们进行深度对比。 首先,我得回忆这三种知识表示方法的基本概念和特点。 (1)一阶谓词逻辑(FOPL)是基于形式逻辑的,使用谓词、变量、量…...
Tomcat生产服务器性能优化
试想以下这个情景:你已经开发好了一个程序,这个程序的排版很不错,而且有着最前沿的功能和其他一些让你这程序增添不少色彩的元素。可惜的是,程序的性能不怎么地。你也十分清楚,若现在把这款产品退出市场,肯…...
【算法day25】 最长有效括号——给你一个只包含 ‘(‘ 和 ‘)‘ 的字符串,找出最长有效(格式正确且连续)括号子串的长度。
32. 最长有效括号 给你一个只包含 ‘(’ 和 ‘)’ 的字符串,找出最长有效(格式正确且连续)括号子串的长度。 https://leetcode.cn/problems/longest-valid-parentheses/ 2.方法二:栈 class Solution { public:int longestValid…...
Python之变量与数据类型总结
前言 一、基本数据类型 1、整数(int) 2、浮点数(float) 3、布尔值(bool) 4、字符串(str) 二、复合数据类型 1、列表(list) 1.1、列表基础 1.1.1、列…...
【大模型基础_毛玉仁】5.3 附加参数法:T-Patcher
目录 5.3 附加参数法:T-Patcher5.3.1 补丁的位置1)键值存储体2)补丁设计 5.3.2 补丁的形式5.3.3 补丁的实现1)准确性2)局部性 5.3 附加参数法:T-Patcher 附加参数法:通过引入可训练的额外参数实…...
【19期获取股票数据API接口】如何用Python、Java等五种主流语言实例演示获取股票行情api接口之沪深A股实时交易数据及接口API说明文档
在量化分析领域,实时且准确的数据接口是成功的基石。经过多次实际测试,我将已确认可用的数据接口分享给正在从事量化分析的朋友们,希望能够对你们的研究和工作有所帮助,接下来我会用Python、JavaScript(Node.js&…...
RSA 简介及 C# 和 js 实现【加密知多少系列_4】
〇、简介 谈及 RSA 加密算法,我们就需要先了解下这两个专业名词,对称加密和非对称加密。 对称加密:在同一密钥的加持下,发送方将未加密的原文,通过算法加密成密文;相对的接收方通过算法将密文解密出来原文…...
Koordinator-Metric查询
以CollectAllPodMetricsLast()举例,看看koordinator怎样使用tsdb进行查询。 CollectAllPodMetricsLast() GenerateQueryParamsLast()传入metric采集间隔2倍时间调用CollectAllPodMetrics()func CollectAllPodMetricsLast(statesInformer statesinformer.StatesInformer, metr…...
LeetCode1两数之和
**思路:**懒得写了,如代码所示 /*** Note: The returned array must be malloced, assume caller calls free().*/ struct hashTable {int key;//存值int val;//存索引UT_hash_handle hh; }; int* twoSum(int* nums, int numsSize, int target, int* re…...
AOA与TOA混合定位,MATLAB例程,三维空间下的运动轨迹,滤波使用EKF,附下载链接
本文介绍一个MATLAB代码,实现基于 到达角(AOA) 和 到达时间(TOA) 的混合定位算法,结合 扩展卡尔曼滤波(EKF) 对三维运动目标的轨迹进行滤波优化。代码通过模拟动态目标与基站网络&am…...
Java算法模板
合并区间 统计不同区间的元素个数 //合并区间List<Integer> result new ArrayList<>();int start intervals.get(0)[0];int end intervals.get(0)[1];for(int i1;i<intervals.size();i){int[] curr intervals.get(i);if(curr[0]>end){//不能合并&…...
软件架构设计中的软件过程模型初识
软件架构设计中的软件过程模型是指导软件开发过程的框架,它们定义了软件开发的不同阶段、活动、任务和角色。结合具体的使用场景,可以更好地理解这些模型如何在实际项目中应用。以下将详细介绍几种常见的软件过程模型,并结合典型场景进行讲解…...
征程 6E mipi tx 系列之方案介绍
MIPI TX 到车机显示系统设计指南 IDE 介绍 征程 6 IDE 架构图 IDE(Image Display Engine)包含图像显示单元(Image Display Unit)、图像数据输出模块(MIPI CSI2 Device 和 MIPI DSI)。通过 IDU 从内存中读…...
std::reference_wrapper 和 std::function的详细介绍
关于 std::reference_wrapper 和 std::function 的详细介绍及具体测试用例: 1. std::reference_wrapper(引用包装器) 核心功能 包装引用:将引用转换为可拷贝、可赋值的对象支持隐式转换:可自动转换为原始引用类型容器…...
【day4】数据结构刷题 树
6-1 二叉树的遍历 函数接口定义: void InorderTraversal( BinTree BT ); void PreorderTraversal( BinTree BT ); void PostorderTraversal( BinTree BT ); void LevelorderTraversal( BinTree BT ); 其中BinTree结构定义如下: typedef struct TNode *Po…...
基于Selenium的IEEE Xplore论文数据爬取实战指南
基于Selenium的IEEE Xplore论文数据爬取实战指南 一、项目背景与目标 IEEE Xplore作为全球知名的学术资源平台,收录了大量高质量科技文献。本教程将演示如何通过Python的Selenium库实现: 自动化获取指定领域论文列表(以"构音障碍"为例)完整提取论文标题、摘要、…...
Vue2 项目将网页内容转换为图片并保存到本地
🌟 前言 欢迎来到我的技术小宇宙!🌌 这里不仅是我记录技术点滴的后花园,也是我分享学习心得和项目经验的乐园。📚 无论你是技术小白还是资深大牛,这里总有一些内容能触动你的好奇心。🔍 &#x…...
flutter 专题 七十一 Flutter 自定义单选控件
在Flutter 应用开发中,经常会遇到各种单选效果,虽然官方提供了Radio组件,但是并不能满足我们实际的开发需求,所以往往还需要自定义控件才能满足平时的开发需求。下面就平时开发中用到的单选进行介绍: 自定义SegmentBa…...
质因数个数--欧拉函数中统计纯素数
和互质数不同,这里统计的是纯素数部分 就是x/i那一部分 #include<bits/stdc.h> using namespace std; #define N 100011 typedef long long ll; typedef pair<ll,int> PII; int n,m,k; ll eular(ll x) { ll an0;ll px;for(ll i2;i*i<x;i){if(x%i…...
RAG基建之PDF解析的“无OCR”魔法之旅
PDF文件转换成其他格式常常是个大难题,大量的信息被锁在PDF里,AI应用无法直接访问。如果能把PDF文件或其对应的图像转换成结构化或半结构化的机器可读格式,那就能大大缓解这个问题,同时也能显著增强人工智能应用的知识库。 嘿,各位AI探险家们!今天我们将踏上了一段奇妙的…...
Web开发:数据的加密和解密
一、常见通用术语解析 加盐:在密码中加入随机数据,提高安全性。摘要:固定长度的输出,用于数据完整性验证。加密:将数据转换为不可读形式,确保安全。撞库:通过暴力破解比对常见密码的攻击方式。…...
从零开始研发GPS接收机连载——15、使用新射频成功打卡日本地标
提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档 从零开始研发GPS接收机连载——15、使用新射频成功打卡日本地标 前言MAX2771配置测试MAX2771完整程序测试 前言 话说笔者花了一笔巨资买了一个指甲盖般大小的MAX2771射频板&…...
linux压缩指令
今天我们来了解一下linux压缩指令,压缩是我们文件传输的一种重要手段,对此,我们是必须学习压缩指令的,那么话不多说,来看. 1.grep过滤查找,管道符,“|”,表示将前一个命令的处理结果输出传递给后面的命令处理。 基本语法&#x…...
智能提示词生成器:助力测试工程师快速设计高质量测试用例
在软件测试中,测试用例设计方法的选择和实施是确保软件质量的重要步骤。测试工程师经常需要根据不同的测试场景、参数维度和业务需求,设计出覆盖率高且有效的测试用例。然而,设计测试用例并非易事,特别是在面对复杂的业务逻辑时。 为了帮助测试工程师高效生成测试用例提示…...
QML中使用Image显示图片和使用QQuickItem显示图片
在QML中显示图片时,Image元素和自定义QQuickItem有不同的特性和适用场景。以下是两者的详细对比及性能分析: 1. Image 元素 优点: 声明式语法:简单直观,适合静态图片或简单动态需求 Image {source: "image.png&…...
若依赖前端处理后端返回的错误状态码
【背景】 后端新增加了一个过滤器,用来处理前端请求中的session 若依赖存放过滤器的目录:RuoYi-Vue\ruoyi-framework\src\main\java\com\ruoyi\framework\security\filter\ 【问题】 后端返回了一个状态码为403的错误,现在前端需要处理这…...
C++23:现代C++的模块化革命与零成本抽象新高度
以下代码为伪代码,仅供参考 一、标准库的范式突破 1. std::expected:类型安全的错误处理 std::expected<DataPacket, ErrorCode> parsePacket(ByteStream& stream) {if (stream.header_valid()) return decode_packet(stream);elsereturn s…...
【嵌入式学习3】TCP服务器客户端 - UDP发送端接收端
目录 1、TCP TCP特点 TCP三次握手(建立TCP连接): TCP四次握手【TCP断开链接的时候需要经过4次确认】: TCP网络程序开发流程 客户端开发:用户设备上的程序 服务器开发:服务器设备上的程序 2、UDP 为…...
《Spring Cloud Eureka 高可用集群实战:从零构建高可靠性的微服务注册中心》
从零构建高可用 Eureka 集群 | Spring Cloud 微服务架构深度实践指南 本文核心内容基于《Spring Cloud 微服务架构开发》第1版整理,结合生产级实践经验优化 实验环境:IntelliJ IDEA 2024 | JDK 1.8| Spring Boot 2.1.7.RELEASE | Spring Cloud Greenwich…...
Dust3r、Mast3r、Fast3r
目录 一.Dust3r 1.简述 2.PointMap与ConfidenceMap 3.模型结构 4.损失函数 5.全局对齐 二.Mast3r 1.简述 2.MASt3R matching 3.MASt3R sfm 匹配与标准点图 BA优化 三.Fast3r 1.简述 2.模型结构 3.损失函数 三维重建是计算机视觉中的一个高层任务,包…...
HTML5 Web SQL 数据库学习笔记
HTML5 的 Web SQL 数据库曾是一种用于在浏览器客户端存储数据的技术,但目前已被废弃。尽管如此,了解其基本概念和操作方法仍具有一定的学习价值。以下是关于 Web SQL 数据库的学习笔记。 一、Web SQL 数据库概述 1.1 状态与替代方案 Web SQL API 已被…...
Plastiform复制胶泥:高精度表面复制与测量的高效工具
在工业制造和质量检测领域,表面复制和测量是确保产品质量的关键环节。Plastiform复制胶泥作为一种创新材料,凭借其出色的性能和多样化的应用,为用户提供了可靠的解决方案。它能够快速捕捉复杂表面的细节,确保测量结果的准确性&…...
安装 `torch-sparse` 和 `torch-cluster`
✅ 安装 torch-sparse 和 torch-cluster 请直接运行下面这条 一行命令 来装 PyG 剩余依赖(适配我已装好的 PyTorch 2.5.1cpu): pip install torch-sparse torch-cluster -f https://data.pyg.org/whl/torch-2.5.1cpu.html✅ 或者自己去官网…...
Linux之基础知识
目录 一、环境准备 1.1、常规登录 1.2、免密登录 二、Linux基本指令 2.1、ls命令 2.2、pwd命令 2.3、cd命令 2.4、touch命令 2.5、mkdir命令 2.6、rmdir和rm命令 2.7man命令 2.8、cp命令 2.9、mv命令 2.10、cat命令 2.11、echo命令 2.11.1、Ctrl r 快捷键 2…...
[mlr3] Bootstrap与交叉验证k-fold cross validation
五折交叉验证因其无放回分层抽样和重复验证机制,成为超参数调优的首选; 而Bootstrap因有放回抽样的重复性和验证集的不稳定性,主要服务于参数估计(置信区间的计算)而非调优。 实际应用中,可结合两者优势&am…...
自动化构建攻略:Jenkins + Gitee 实现 Spring Boot 项目自动化构建
Jenkins Gitee 实现 Spring Boot 项目自动化构建 环境准备安装配置jdk安装配置maven安装git安装配置Jenkins 测试构建测试自动化触发 环境准备 云服务器环境: 系统版本:Ubuntu 24.04 64位ecs规格:4核(vCPU)8 GiB公网带宽:10Mbi…...
Python 中的不可变数据类型的解析
# Python 中的不可变数据类型的解析 在 Python 的世界里,数据类型扮演着至关重要的角色。根据数据是否可以在创建后被修改,Python 数据类型可分为可变和不可变两类。本文将聚焦于不可变数据类型,详细介绍它们的特点,并结合具体实例…...
【Kafka】分布式消息队列的核心奥秘
文章目录 一、Kafka 的基石概念主题(Topic)分区(Partition)生产者(Producer)消费者(Consumer) 二、Kafka 的架构探秘Broker 集群副本机制 三、Kafka 的卓越特性高…...
基于Promise链式调用的多层级请求性能优化
代码优化-循环嵌套关联请求 1. 背景 在实际开发中,我们经常会遇到需要嵌套关联请求的场景,比如: 获取项目列表获取项目详情获取项目进度 2. 问题 在这种场景下,我们可能会遇到以下问题: 串行请求瀑布流ÿ…...
RuoYi基础学习
1 若依搭建 前后端分离版本:RuoYi-Vue利用SpringBoot作为后端开发框架,与Vue.js结合,实现了前后端分离的开发模式。这种架构有助于提高开发效率,前后端可以独立开发和部署,更适合现代化的Web应用开发。 RuoYi-Vue3&a…...
解决关于原生gmssl无法直接输出sm2私钥明文的问题
解决关于原生gmssl无法直接输出sm2私钥明文的问题 问题描述解决方法解决方法一解决方法二 问题描述 通过gmssl生成sm2公私钥对时,输出的是加密的sm2私钥,无法获取到SM2私钥明文。 解决方法 解决方法一 手动解密: 解决方法二 修改源码&…...
AT24Cxx移植第三方库到裸机中使用
简介 MCU : STM32F103C8T6 库: HAL库裸机开发 EEPROM : AT24C02, 256Byte容量,I2C接口 电路图 AT24C02 电路图 电路图引用 逻辑直接读写 // 写入数据到 EEPROM HAL_StatusTypeDef EEPROM_WriteByte(uint16_t MemAddress, uint8_t Data) {// 发送数据uint8_t …...
【落羽的落羽 C++】内存区域、C++的内存管理
文章目录 一、内存区域二、C的内存管理1. new和delete2. new和delete的特点3. 实现的原理 一、内存区域 C语言和C中,我们常把计算机的内存分为不同的区域,有各自不同的功能: 栈区:存放函数的局部变量、参数、返回地址等。堆区&a…...
星际旅行(去年蓝桥杯省赛b组-第7题)
题目链接: 蓝桥账户中心 朋友分享给我一道题,我刚开始其实先用dfs写,但是直接就超时了(很大的一部分原因是截图中没有数据范围) #include<bits/stdc.h> using namespace std; const int MAXN 1e97; vector<int> graph[MAXN]; bool visite…...
转发和重定向的区别详解
转发(Forward)和重定向(Redirect)是 Web 开发中两种常用的请求处理方式,主要用于将客户端请求从一个资源转移到另一个资源。它们在实现机制、行为表现和应用场景上有显著区别,以下是对两者的详细解析&#…...
HarmonyOS NEXT——【鸿蒙相册图片以及文件上传Picker封装】
1、鸿蒙系统文件/图片上传base64: 鸿蒙应用需要上传图片或者文件时,由于更高的安全性与更严谨的访问权限,通常无法直接从系统相册或文件管理中直接上传,因此我们可以通过picker对象去拉起相册访问的能力,引导用户选择…...
Java中文件copy的5种方式
Java中文件copy的5种方式 传统字节流缓冲流jdk7 Files.copy通道(零拷贝)内存映射对比 传统字节流 缓冲流 jdk7 Files.copy 通道(零拷贝) 内存映射 对比...
Nacos Client 模块的作用是什么?是如何与 Server 端通信的?
Nacos Client 模块是 Nacos 架构中的重要组成部分,它负责与 Nacos Server 端进行交互,实现服务注册、服务发现、配置管理等核心功能。 可以将 Nacos Client 理解为 Nacos 提供给应用程序使用的 SDK。 Nacos Client 模块的主要作用: 服务注册 (Service R…...