CountDownLatch、Semaphore详解——深入探究CountDownLatch、Semaphore源码
这篇文章将会详细介绍基于AQS实现的两个并发类CountDownLatch和Semaphore,通过深入底层源代码讲解其具体实现。
目录
CountDownLatch
countDown()
await()
Semaphore
Semaphore类图
Semaphore的应用场景
acquire()
tryAcquire()
CountDownLatch
/*** A synchronization aid that allows one or more threads to wait until* a set of operations being performed in other threads completes.*/
上面是CountDownLatch这个API的前两行注释,一句话已经说明了这个类的作用。
一种同步辅助工具,允许一个或多个线程等待其他线程正在执行的一组操作完成。
CountDownLatch是一个计数器,通过的构造方法指定初始计数器的值,调用CountDownLatch的countDown()方法让计数器的值减少1,当计数器的值变成0时,调用它的await()方法阻塞的当前线程会被释放(其实就是从for循环中结束返回),继续执行剩余的代码。
这个类的结构很简单,就不画类图了,直接贴出代码
package java.util.concurrent;import java.util.concurrent.locks.AbstractQueuedSynchronizer;public class CountDownLatch {private static final class Sync extends AbstractQueuedSynchronizer {private static final long serialVersionUID = 4982264981922014374L;Sync(int count) {setState(count);}int getCount() {return getState();}protected int tryAcquireShared(int acquires) {return (getState() == 0) ? 1 : -1;}protected boolean tryReleaseShared(int releases) {// Decrement count; signal when transition to zerofor (;;) {int c = getState();if (c == 0)return false;int nextc = c-1;if (compareAndSetState(c, nextc))return nextc == 0;}}}private final Sync sync;public CountDownLatch(int count) {if (count < 0) throw new IllegalArgumentException("count < 0");this.sync = new Sync(count);}public void await() throws InterruptedException {sync.acquireSharedInterruptibly(1);}public boolean await(long timeout, TimeUnit unit)throws InterruptedException {return sync.tryAcquireSharedNanos(1, unit.toNanos(timeout));}public void countDown() {sync.releaseShared(1);}public long getCount() {return sync.getCount();}public String toString() {return super.toString() + "[Count = " + sync.getCount() + "]";}}
CountDownLatch是通过AQS的实现类Sync来实现这个计数器功能的,通过AQS的state属性保存计数器的值。
因为这里的计数器值state是共享的,所以重写了AQS的tryAcquireShared()和tryReleaseShared()方法。
为什么要重写这两个方法呢?
因为AQS里的几个重要的方法aquire()和release()会根据当前是独占模式还是共享模式去调用对应的tryAcquire()/tryAcquireShared()、tryRelease()/tryReleaseShared()方法。
而Java已经约定了,继承AQS应该指明state属性的语义:
- 在CountDownLatch中,state值表示计数器的值;
- 在Semaphore中,state值表示许可证的数量;
接下来,介绍CountDownLatch中的两个最重要的方法:
countDown()
让计数器的值减1
public void countDown() {sync.releaseShared(1);}
在这个方法里调用了releaseShared()方法,因为静态内部类Sync没有重写这个方法,所以调用的是超类AbstractQueuedSynchronizer的方法。
public final boolean releaseShared(int arg) {if (tryReleaseShared(arg)) {doReleaseShared();return true;}return false;}
这个方法里先调用tryReleaseShared()方法,因为Sync重写了这个方法,所以调用的是Sync的方法。
protected boolean tryReleaseShared(int releases) {for (;;) {// 获取state值,如果是0,直接返回falseint c = getState();if (c == 0) {return false;}// 通过Unsafe工具类的CAS方法尝试修改state的值为state - 1int nextc = c - 1;if (compareAndSetState(c, nextc)) {// 修改完成之后,根据修改之前的state值是否为1返回true或falsereturn nextc == 0;}}}
await()
阻塞当前线程,直到state值变成0才恢复。
public void await() throws InterruptedException {sync.acquireSharedInterruptibly(1);
}
在方法内部调用了AQS的acquireSharedInterruptibly()方法,Interruptibly后缀的方法都可以被中断。
public final void acquireSharedInterruptibly(int arg) throws InterruptedException {if (Thread.interrupted())throw new InterruptedException();if (tryAcquireShared(arg) < 0)doAcquireSharedInterruptibly(arg);}
在这个方法中,先调用Thread类的静态方法interrupted()判断当前线程是否被中断,如果当前线程中断状态为true,则清除线程的中断标记并返回true。否则返回false。如果当前线程是被中断的状态,则抛出中断异常返回。关于interrupted()方法的详细介绍,请参考文章
interrupt()、interrupted()和isInterruptd()的区别
https://blog.csdn.net/heyl163_/article/details/132138422如果当前线程是正常的状态,调用tryAcquireShared()方法,因为Sync重写了这个方法,所以调用的是Sync的tryAcquireShared()方法。这个方法就是判断当前state属性值是否为0,如果不是0就返回-1,否则返回1
protected int tryAcquireShared(int acquires) {return (getState() == 0) ? 1 : -1;}
继续回到上面的代码,当state不为0时,会执行doAcquireSharedInterruptibly()方法,注意,这里的一个死循环for会让当前线程一直阻塞在这里,直到tryAcquireShared()获取到的返回值大于0,也就是1时才退出循环,而根据上面的方法,此时state值为0。
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 >= 0) {setHeadAndPropagate(node, r);p.next = null; // help GCfailed = false;return;}}if (shouldParkAfterFailedAcquire(p, node) && parkAndCheckInterrupt())throw new InterruptedException();}} finally {if (failed)cancelAcquire(node);}}
总结:这里的等待是通过无条件的for循环让当前线程一直等待,直到state的值变为0才退出循环返回。所以,在这里可以替代线程的join()方法使用,这也是CountDownLatch的主要用途。
Semaphore
A counting semaphore.Conceptually, a semaphore maintains a set of permits. Each acquire blocks if necessary until a permit is available, and then takes it. Each release adds a permit, potentially releasing a blocking acquirer. However, no actual permit objects are used; the Semaphore just keeps a count of the number available and acts accordingly.
一个计数器信号量,一个Semaphore包含一组许可证的集合,每个获取器都会在必要时阻塞,直到有许可证可用,然后获取它。每次释放都会增加一个许可,从而有可能释放阻塞的获取者。不过, Semaphore 并不使用实际的许可证对象;它只是对可用数量进行计数,并采取相应的行动。
以上是Semaphore的简单介绍,在源码的注释最开头就能看到。。
Semaphore类图

Semaphore的应用场景
接下来通过一个案例来介绍Semaphore适用于什么场景
在广州过节和朋友去吃火锅就知道,桌子数量是固定的,一个店就那么大,就只有x桌。去到店里基本上是要排队的,要等取号在自己的号数前面的人吃完出来才能空出来桌位,否则就要在那里一直等。
上面的场景,假如店里刚好坐满,这时候突然来了1对情侣,这时候因为店里的人还没吃完,要等最少一桌的人用完餐之后出来才能轮到这队情侣用餐。
其实这里的操作就是acquire(),店里的桌数就是Semaphore的初始许可数。
acquire()
public void acquire() throws InterruptedException {sync.acquireSharedInterruptibly(1);
}
方法内部调用了以下方法,当线程被中断时,清除中断标记,抛出中断异常。否则,调用tryAcquireShared()方法尝试得到一个许可证。
public final void acquireSharedInterruptibly(int arg) throws InterruptedException {if (Thread.interrupted())throw new InterruptedException();if (tryAcquireShared(arg) < 0)doAcquireSharedInterruptibly(arg);}
这里会根据调用构造方法指定的公平锁还是非公平锁调用FairSync或NoneFairSync的tryAcquireShared()方法。
如果是使用一个参数的构造方法实例化,则使用默认的非公平锁,否则根据参数fair来决定是否公平锁。
public Semaphore(int permits) {sync = new NonfairSync(permits);
}public Semaphore(int permits, boolean fair) {sync = fair ? new FairSync(permits) : new NonfairSync(permits);
}
以默认的NoneFairSync为例,tryAcquireShared()方法会调用其超类Sync中定义的nonfairTryAcquireShared()方法。
protected int tryAcquireShared(int acquires) {return nonfairTryAcquireShared(acquires);
}final int nonfairTryAcquireShared(int acquires) {for (;;) {int available = getState();int remaining = available - acquires;if (remaining < 0 || compareAndSetState(available, remaining))return remaining;}
}
nonfairTryAcquireShared()方法中会一直尝试去扣减AQS中的state值,也就是信号量中的许可证的数量。
回到acquireSharedInterruptibly()方法,当我们尝试扣减state之后state小于0,也就是许可证数量不够了,就会执行doAcquireSharedInterruptibly()方法,这个方法在CountDownLatch里也讲了,主线程会一直在for循环里出不去,相当于阻塞在这里了,直到申请许可证成功,也就是上面的方法返回了不小于0的数。
public final void acquireSharedInterruptibly(int arg) throws InterruptedException {if (Thread.interrupted())throw new InterruptedException();if (tryAcquireShared(arg) < 0)doAcquireSharedInterruptibly(arg);}private void doAcquireInterruptibly(int arg)throws InterruptedException {final Node node = addWaiter(Node.EXCLUSIVE);boolean failed = true;try {for (;;) {final Node p = node.predecessor();if (p == head && tryAcquire(arg)) {setHead(node);p.next = null; // help GCfailed = false;return;}if (shouldParkAfterFailedAcquire(p, node) && parkAndCheckInterrupt())throw new InterruptedException();}} finally {if (failed)cancelAcquire(node);}}
tryAcquire()
尝试获取许可证,如果许可证数量足够,则返回true,否则返回false。
public boolean tryAcquire() {return sync.nonfairTryAcquireShared(1) >= 0;
}
相关文章:
CountDownLatch、Semaphore详解——深入探究CountDownLatch、Semaphore源码
这篇文章将会详细介绍基于AQS实现的两个并发类CountDownLatch和Semaphore,通过深入底层源代码讲解其具体实现。 目录 CountDownLatch countDown() await() Semaphore Semaphore类图 Semaphore的应用场景 acquire() tryAcquire() CountDownLatch /*** A synchroni…...
windows生成ios证书的方法
使用hbuilderx的uniapp框架开发ios应用,在测试阶段和发布阶段,需要ios证书进行打包,云打包的界面提供了生成ios证书的教程,但是教程令人很失望,它只能使用mac电脑来生成ios证书。假如没有mac电脑,就无法安照…...
【小沐学Unity3d】3ds Max 骨骼动画制作(Physique 修改器)
文章目录 1、简介2、Physique 工作流程3、Physique 对象类型4、Physique 增加骨骼5、Physique 应用和初始化6、Physique 顶点子对象7、Physique 封套子对象8、设置关键点和自动关键点模式的区别8.1 自动关键点8.2 设置关键点 结语 1、简介 官方网址: https://help.…...
生态项目|Typus如何用Sui特性制作动态NFT为DeFi赋能
对于许多人来说,可能因其涉及的期权、认购和价差在内的DeFi而显得晦涩难懂,但Typus Finance找到了一种通过动态NFT使体验更加丰富的方式。Typus NFT系列的Tails为用户带来一个外观逐渐演变并在平台上提升活动水平时获得新特权的角色。 Typus表示&#x…...
IOS打包上架AppStore被驳回信息记录
1:错误码5.2.1错误信息如下 Your app includes content or features from 公司名, or is marketed to control external hardware from 公司名, without the necessary authorization. The inclusion of third-party content within your app, whether retrieved fr…...
【Python自学笔记】Python好用的模块收集(持续更新...)
文章目录 日志模块钉钉机器人命令助手持续更新中,如果您有其他实用好用的模块欢迎留言...日志模块 写代码离不开日志,自定义一个理想的日志对于小白来说可能是一件很反锁的事情,就像我刚学习Python的时候自己写的一个自定义日志,为了解决这个痛点,今天就和大家分享一个可以…...
在springboot中配置mybatis(mybatis-plus)mapper.xml扫描路径的问题
我曾经遇到过类似问题: mybatis-plus的mapper.xml在src/main/java路径下如何配置pom.xml和application.yml_idea 把mapper文件放到java下如何配置_梓沂的博客-CSDN博客 当时只是找到解决问题的办法,但对mybatis配置来龙去脉并未深入了解,所…...
c++搜索剪枝常见方法与技巧
目录 搜索剪枝常见方法与技巧 关键字 搜索方法,剪枝 摘要 正文 小结 程序 参考书目 搜索剪枝常见方法与技巧 关键字 搜索方法,剪枝 摘要 搜索是计算机解题中常用的方法,它实质上是枚举法的应用。由于它相当于枚举法,所以其效率是相当地的。因此…...
YOLO V5 和 YOLO V8 对比学习
参考文章: 1、YOLOv5 深度剖析 2、如何看待YOLOv8,YOLOv5作者开源新作,它来了!? 3、anchor的简单理解 完整网络结构 YOLO v5和YOLO v8的Head部分 YOLO v8的Head 部分相比 YOLOv5 改动较大,换成了目前主流的解耦头结构…...
【Git】(六)子模块跟随主仓库切换分支
场景 主仓库:TestGit 子模块:SubModule 分支v1.0 .gitmodules文件 [submodule "Library/SubModule"]path Library/SubModuleurl gitgitee.com:sunriver2000/SubModule.gitbranch 1.0.0.0 分支v2.0 .gitmodules文件 [submodule "Li…...
开源的经济影响:商业与社区的平衡
🌷🍁 博主猫头虎 带您 Go to New World.✨🍁 🦄 博客首页——猫头虎的博客🎐 🐳《面试题大全专栏》 文章图文并茂🦕生动形象🦖简单易学!欢迎大家来踩踩~🌺 &a…...
数据库复习整理
1.group by与where 一,group by 字句也和where条件语句结合在一起使用。当结合在一起时,where在前,group by 在后。 即先对select xx from xx的记录集合用where进行筛选,然后再使用group by 对筛选后的结果进行分组 使用having字句…...
开始MySQL之路——MySQL安装和卸载
MySQL的介绍 MySQL数据库管理系统由瑞典的DataKonsultAB公司研发,该公司被Sun公司收购,现在Sun公司又被Oracle公司收购,因此MySQL目前属于Oracle旗下产品。 MySQL所使用的SQL语言是用于访问数据库的最常用标准化语言。MySQL软件采用了双授权…...
pxe网络装机
PXE是什么? 批量装机系统,网络安装linux操作系统。需要客户端的网卡支持pxe网络启动。 PXE的组件: vsftpd/httpd/nfs 负责提供系统的安装文件 tftp 负责提供系统安装前的引导文件与内核文件 dhcp 负责提供客户端的IP地址分配与pxe引…...
【数据库事务】
数据库事务 何为事务事务的特性原子性 Atomicity一致性 Consistency隔离性 IsolationRead UncommittedRead CommittedRepeatable ReadSerializable 持久性 Durability功能快捷键合理的创建标题,有助于目录的生成如何改变文本的样式插入链接与图片如何插入一段漂亮的…...
Apache Tomcat
在Java中,如果您想使用 Apache Tomcat 作为服务器容器,您需要从 Apache Tomcat 官方网站(https://tomcat.apache.org)下载并导入 Tomcat 的相关 JAR 文件。 以下是使用 Tomcat 类创建和配置 Tomcat 服务器的示例代码:…...
python类
python是一种面向对象的变成语言。 python几乎所有的东西都是对象,包括对象和属性。 一.类的定义 python类的定义: class ClassName:pass: 实例: 注意: 类中的函数称为方法,有关于函数的一切适用于方法&…...
SpringBoot + layui 框架实现一周免登陆功能
✅作者简介:2022年博客新星 第八。热爱国学的Java后端开发者,修心和技术同步精进。 🍎个人主页:Java Fans的博客 🍊个人信条:不迁怒,不贰过。小知识,大智慧。 💞当前专栏…...
如何使用Unity制作一个国际象棋
LinnoChess1.0 该项目旨在做一些Unity小游戏项目开发来练练手 如果有更新建议请私信RWLinno 项目地址:https://github.com/RWLinno/LinnoChess 目前效果 能够正常下棋;能够编辑棋盘;能够SL棋局;能够记录棋谱;能够显…...
下岗吧,Excel
ChatGPT的诞生使Excel公式变得过时。通过使用 ChatGPT 的代码解释器你可以做到: 分析数据创建图表 这就像用自然语言与电子表格交谈一样。我将向大家展示如何使用 ChatGPT 执行此操作并将结果导出为Excel格式: 作为示例,我将分析并创建美国…...
WELearn网课助手:5分钟告别熬夜刷课,实现高效学习自由的终极指南
WELearn网课助手:5分钟告别熬夜刷课,实现高效学习自由的终极指南 【免费下载链接】WELearnHelper 显示WE Learn随行课堂题目答案;支持班级测试;自动答题;刷时长;基于生成式AI(ChatGPT)的答案生成 项目地址…...
现代C++中的音频引擎缓冲调度实践
现代C中的音频引擎缓冲调度实践音频引擎与普通后台任务系统不同,它更强调稳定时序和低抖动。哪怕平均性能很好,只要某次回调超时,就会产生爆音、卡顿或丢帧。因此 C 音频处理的重点往往是缓冲调度和实时约束。一个简化的音频回调接口…...
17个AI新闻站吸4.4万访客,10美元即可搭建,滥用AI威胁原创媒体!
《佛罗里达论坛报》揭秘AI伪媒体系统智东西5月15日报道,当地时间5月14日,美国调查媒体《佛罗里达论坛报》披露,南佛州《南佛罗里达标准报》是由AI批量生成的伪媒体系统。该网站包装本地新闻团队,用AI生成记者头像、履历和邮箱&…...
当ChIP-seq遇见单细胞:技术原理、应用场景与未来展望,一次给你讲清楚
当单细胞分辨率重塑表观遗传学:scChIP-seq的技术突破与应用全景 表观遗传学研究正经历一场分辨率革命。过去十年间,科学家们不得不依赖数百万细胞才能绘制组蛋白修饰或转录因子结合的全局图谱,这种"群体平均"的视角掩盖了细胞间异…...
Nodejs服务端如何配置Taotoken的OpenAI兼容SDK
🚀 告别海外账号与网络限制!稳定直连全球优质大模型,限时半价接入中。 👉 点击领取海量免费额度 Nodejs服务端如何配置Taotoken的OpenAI兼容SDK 对于使用Node.js构建服务端应用的开发者来说,集成大模型能力正变得日益…...
3分钟快速解决iPhone USB网络共享问题:实用高效驱动安装指南
3分钟快速解决iPhone USB网络共享问题:实用高效驱动安装指南 【免费下载链接】Apple-Mobile-Drivers-Installer Powershell script to easily install Apple USB and Mobile Device Ethernet (USB Tethering) drivers on Windows! 项目地址: https://gitcode.com/…...
骨传导耳机品牌Mojawa完成数千万元A+轮融资,发力AI运动智能平台
硬氪获悉,苏州索迩电子技术有限公司近日完成数千万元人民币的A轮融资,由正海资本领投。资金将用于拓展海外线下渠道和推进产品AI智能化研发。骨传导耳机市场增长显著在音频产品市场,骨传导耳机因无线和开耳式聆听技术需求增加而显著增长。202…...
车载毫米波雷达性能验证(1)_基于雷达模拟器的目标检测精度与可靠性测试
1. 车载毫米波雷达性能验证的核心逻辑 第一次接触车载毫米波雷达测试时,我被各种专业术语搞得晕头转向。直到后来才发现,性能验证的本质就是回答两个问题:测什么和怎么测。这就像买手机要关注摄像头像素和跑分一样,雷达测试也要抓…...
税调企业与所在区县税务局的距离
税调企业与所在区县税务局的距离2007-2020数据包含:2007~2020年税调企业与所在区县税务局的距离_km.dta数据包含如下变量:sdid、与税务局的大圆距离_km、税务局经度、经度、纬度、省、省代码、市、市代码、县、县代码、税务局纬度、组织机构代…...
硅与锗PN结实战对比:手把手测量导通电压VF与温度系数
硅与锗PN结实战对比:手把手测量导通电压VF与温度系数 在电子工程实践中,PN结的特性测量是理解半导体器件行为的基础。硅(Si)和锗(Ge)作为两种经典半导体材料,其PN结在导通电压(VF)和温度特性上表现出显著差异。本文将带领读者通过实际测量&a…...
