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格式: 作为示例,我将分析并创建美国…...

黑马点评环境搭建导入
一开始配置maven的时候,发现怎么都无法查看maven的版本,后来才知道是JAVA_HOME的问题,开头多了一个空格(因为我是直接复制过去的),然后搜网上通过命令行可以看到肉眼看不到的bug。 通过命令行的方式改正确后…...

交换机端口安全
文章目录 一、802.1X认证1. 定义和起源2. 认证方式本地认证远程集中认证 3. 端口接入控制方式基于端口认证基于MAC地址认证 二、端口隔离技术1. 隔离组2. 隔离原理3. 应用场景 首先可以看下思维导图,以便更好的理解接下来的内容。 一、802.1X认证 1. 定义和起源 8…...

【力扣】63. 不同路径 II <动态规划>
【力扣】63. 不同路径 II 一个机器人位于一个 m m m x n n n 网格的左上角 (起始点在下图中标记为 “Start” )。 机器人每次只能向下或者向右移动一步。机器人试图达到网格的右下角(在下图中标记为 “Finish”)。 现在考虑网格…...

【Linux】JumpServer 堡垒机远程访问
文章目录 前言1. 安装Jump server2. 本地访问jump server3. 安装 cpolar内网穿透软件4. 配置Jump server公网访问地址5. 公网远程访问Jump server6. 固定Jump server公网地址 前言 JumpServer 是广受欢迎的开源堡垒机,是符合 4A 规范的专业运维安全审计系统。JumpS…...

WebGPT VS WebGPU
推荐:使用 NSDT编辑器 快速搭建3D应用场景 随着WebGPU的引入,Web开发发生了有趣的转变,WebGPU是一种新的API,允许Web应用程序直接访问设备的图形处理单元(GPU)。这种发展意义重大,因为 GPU 擅长…...

【Flutter】Flutter 使用 collection 优化集合操作
【Flutter】Flutter 使用 collection 优化集合操作 文章目录 一、前言二、安装和基本使用三、算法介绍四、如何定义相等性五、Iterable Zip 的使用六、优先队列的实现和应用七、包装器的使用八、完整示例九、总结 一、前言 大家好!我是小雨青年,今天我要…...

【核心复现】基于合作博弈的综合能源系统电-热-气协同优化运行策略(Matlab代码实现)
💥💥💞💞欢迎来到本博客❤️❤️💥💥 🏆博主优势:🌞🌞🌞博客内容尽量做到思维缜密,逻辑清晰,为了方便读者。 ⛳️座右铭&a…...

【设计模式】Head First 设计模式——抽象工厂模式 C++实现
设计模式最大的作用就是在变化和稳定中间寻找隔离点,然后分离它们,从而管理变化。将变化像小兔子一样关到笼子里,让它在笼子里随便跳,而不至于跳出来把你整个房间给污染掉。 设计思想 提供一个接口,让该接口负责创建一…...

pdf怎么转换成jpg图片?
随着数字文档的广泛应用,将PDF转换为JPG图片格式成为了一个常见的需求。无论是为了在网页上展示内容,还是为了与他人分享图片,以下是一些简单的方法,帮助您将PDF文件快速转换为高质量的JPG图片。 方法一:在线PDF转JPG…...

远程访问Linux的DataEase数据可视化分析,有哪些推荐的工具?
DataEase 是开源的数据可视化分析工具,帮助用户快速分析数据并洞察业务趋势,从而实现业务的改进与优化。是开源的数据可视化分析工具,帮助用户快速分析数据并洞察业务趋势,从而实现业务的改进与优化。 在本地搭建后,借助cpolar 内…...