Semaphore 源码解读
一、Semaphore
Semaphore
通过设置一个固定数值的信号量,并发时线程通过 acquire()
获取一个信号量,如果能成功获得则可以继续执行,否则将阻塞等待,当某个线程使用 release()
释放一个信号量时,被阻塞的线程则可以被唤醒重新争抢信号量。根据该特征可以有效控制线程的并发数。
那 Semaphore
是如何控制并发的呢,本篇文章带领大家一起解读下 Semaphore
的源码。
在进行源码分析前,先回顾下 Semaphore
是如何使用的,例如下面一个案例:
public class Test {public static void main(String[] args) {Semaphore semaphore = new Semaphore(3);for (int i = 0; i < 10; i++) {new Thread(() -> {try {semaphore.acquire();System.out.println("线程:" + Thread.currentThread().getName() + " 执行, 当前时间:" + LocalDateTime.now().toString());Thread.sleep(1000);} catch (Exception e) {e.printStackTrace();} finally {semaphore.release();}}, String.valueOf(i)).start();}}
}
运行之后,可以看到下面日志:
可以看到每次都是 3
个并发。
在本专栏前面讲解 AQS
源码的时候提到 Semaphore
是基于 AQS
实现的,那是如何使用的 AQS
呢?
在 AQS
中,如果需要使用AQS
的特征则需要子类根据使用的场景,重写下面方法:
//查询是否正在独占资源,condition会使用
boolean isHeldExclusively()
//独占模式,尝试获取资源,成功则返回true,失败则返回false
boolean tryAcquire(int arg)
//独占模式,尝试释放资源,成功则返回true,失败则返回false
boolean tryRelease(int arg)
//共享模式,尝试获取资源,如果返回负数表示失败,否则表示成功。
int tryAcquireShared(int arg)
//共享模式,尝试释放资源,成功则返回true,失败则返回false。
boolean tryReleaseShared(int arg)
由于这里 Semaphore
的特性,所以下面我们只需关注共享模式下的几个方法即可。
说明:由于 Semaphore
的实现依赖于 AQS
,因此需要对 AQS
有一定的了解,不了解的小伙伴可以看下这篇对 AQS
源码分析的文章,和当前文章在同一专栏:
https://blog.csdn.net/qq_43692950/article/details/129367736
二、Semaphore 中 Sync、FairSync、NonfairSync
2.1 Sync、FairSync、NonfairSync
在声明 Semaphore
时,有两种方式,一种是使用只有一个 permits
参数的构造函数,一种则需要多增加一个 fair
参数:
new Semaphore(3);
new Semaphore(3, true);
当使用只有一个 permits
参数的构造函数声明时,则是创建了一个 NonfairSync
对象:
通过需要多增加一个 fair
参数的构造函数时,则可以根据传入的 fair
选择创建一个 FairSync
对象:
这里也不难理解 NonfairSync
和 FairSync
其实可以理解为 Semaphore
中的非公平锁和公平锁两种类型。
点到这两个类中,可以看到都继承自 Sync
类:
而 Sync
类,则继承自 AQS
:
到这里,我们就可以寻找几个关键的方法,在AQS
中共享模式下,两大关键的方法是交由子类进行实现的,分别是 tryAcquireShared
尝试获取资源,和 tryReleaseShared
尝试释放资源。
首先来看 tryAcquireShared
尝试获取资源:
通过 Sync
类的实现源码发现并没有重写 tryAcquireShared
方法,那该方法肯定在下面的FairSync
和 NonfairSync
子类中,分别看下源码确实存在重写的方法:
2.2 NonfairSync 下的 tryAcquireShared
这里先分析下 NonfairSync
的 tryAcquireShared
实现逻辑,可以看到又调用了 nonfairTryAcquireShared
就是 Sync
类中的 nonfairTryAcquireShared
,从命名上可以分析出就是非公平锁的尝试获取资源的操作,直观就是非公平锁下获取锁的操作:
进入到 Sync
类中的 nonfairTryAcquireShared
方法中,可以明显看到一个自旋的操作,在循环中首先获取到 AQS
中的共享资源 state
,并对其进行 - acquires
(默认为 1
,后面会进行说明)操作,其实就是 -1
操作,如果减去的值小于 0
或者修改 state
成功,就返回当前减去的值,否则就自旋的方式再次重试:
上一步的操作主要做了什么目的呢,其实从 Sync
的构造方法就可以看出,创建 Semaphore
传递的 permits
参数被赋值给了 AQS
中的 state
,那此时 state
就记录的当前剩余信号量的大小,获取资源就要进行 -1
标识消耗了一个,最后将减去的值返回出去表示剩余的资源,如果信号量小于 0
了,则表示获取资源失败,直观就是获取锁失败。因为在 AQS
中对 tryAcquireShared
方法的判断是小于 0
时,进行线程的入列和挂起等待。
2.3 FairSync下的 tryAcquireShared
在 FairSync
类下的 tryAcquireShared
方法中,和前面 NonfairSync
类似,但不同的是,会首先进行 hasQueuedPredecessors
方法的判断:
下面进到 hasQueuedPredecessors
的方法中,可以看到是由 AQS
提供的方法,主要就是判断当前节点线程的前面是否还有等待的线程,因为 FairSync
实现的是公平锁的原则,如果当前线程前面还有等待线程,则获取锁资源也轮不到自个,让前面的老大先来,所以直接返回 -1
表示获取资源失败:
2.4 tryReleaseShared
到这里已经了解到了 tryAcquireShared
尝试获取资源的逻辑,上面提到了两个重要方法,还有一个 tryReleaseShared
没有分析,还是首先看 Sync
类中是否有重写该方法:
通过源码可以看到,在 Sync
类中就已经对 tryReleaseShared
进行了重写,而 NonfairSync
和 FairSync
中都没有重写该方法,那释放资源就是走的 Sync
类下的 tryReleaseShared
方法:
在该方法同样使用了自旋,首先获取到 AQS
中的共享资源 state
,然后进行 + releases
(默认情况下为 1
,后面会说明),其实就是进行 +1
操作,并使用新的值修改 state
,如果修改失败的话则在自旋中继续修改,直到成功后返回 true
,表示释放资源成功。
看到这里就会发现获取资源和释放资源,无非就是对 AQS
中的共享资源 state
进行操作。理解了这两大核心的方法后,下面就可以看如何运用在 Semaphore
中的了。
三、semaphore.acquire()
通过 semaphore.acquire()
可以获取一个信号量,如果获取不到则阻塞等待,那semaphore.acquire()
主要做了什么呢?
下面点到该方法中,可以看到又调用了 sync.acquireSharedInterruptibly
方法,其实就是 AQS
中的 acquireSharedInterruptibly
方法,注意这里传递的参数为 1
,对应上面括号中的说明:
在 AQS
的 acquireSharedInterruptibly
方法中,首先会使用子类的 tryAcquireShared
方法获取资源,如果资源数小于 0
,则认为获取失败,下面使用 doAcquireSharedInterruptibly
进行加入队列并挂起阻塞:
关于AQS
如何加入队列和挂起,可以参考文章开始的链接中对 AQS
源码的解读。
四、semaphore.release()
上面在获取不到可用的资源时,则会被 AQS
挂起,因此这里还需要进行释放资源。
下面点到 semaphore.release()
方法中,可以看到又调用了 sync.releaseShared
,其实就是 AQS
中的 releaseShared
方法,注意这里参数默认为 1
,对应上面括号中的说明:
在 AQS
的 releaseShared
方法中,会首先调用子类的 tryReleaseShared
释放资源,释放成功后,会使用 doReleaseShared
进行挂起线程的唤醒:
关于releaseShared
方法的源码解读可以参考文章开始的链接中对 AQS
源码的解读。
三、总结
通过阅读 Semaphore
的源码可以发现,大量依赖于 AQS
中提供的方法,如果有阅读过本专栏对 ReentrantLock
锁源码的分析,可以发现相似度极高,都是使用 AQS
所提供的的特征实现某些场景的应用。
Semaphore 源码解读相关推荐
- aqs java 简书,Java AQS源码解读
1.先聊点别的 说实话,关于AQS的设计理念.实现.使用,我有打算写过一篇技术文章,但是在写完初稿后,发现掌握的还是模模糊糊的,模棱两可. 痛定思痛,脚踏实地重新再来一遍.这次以 Java 8源码为基 ...
- AQS同步器源码解读有感
1.前言 AQS(AbstractQueuedSynchronizer)是java.util.concurrent的基础.也是Doug Lea大神为广大java开发作出的卓越贡献.J.U.C中的工具类 ...
- Bert系列(二)——源码解读之模型主体
本篇文章主要是解读模型主体代码modeling.py.在阅读这篇文章之前希望读者们对bert的相关理论有一定的了解,尤其是transformer的结构原理,网上的资料很多,本文内容对原理部分就不做过多 ...
- Bert系列(三)——源码解读之Pre-train
https://www.jianshu.com/p/22e462f01d8c pre-train是迁移学习的基础,虽然Google已经发布了各种预训练好的模型,而且因为资源消耗巨大,自己再预训练也不现 ...
- linux下free源码,linux命令free源码解读:Procps free.c
linux命令free源码解读 linux命令free源码解读:Procps free.c 作者:isayme 发布时间:September 26, 2011 分类:Linux 我们讨论的是linux ...
- nodeJS之eventproxy源码解读
1.源码缩影 !(function (name, definition) { var hasDefine = typeof define === 'function', //检查上下文环境是否为AMD ...
- PyTorch 源码解读之即时编译篇
点击上方"AI遇见机器学习",选择"星标"公众号 重磅干货,第一时间送达 作者丨OpenMMLab 来源丨https://zhuanlan.zhihu.com/ ...
- Alamofire源码解读系列(九)之响应封装(Response)
本篇主要带来Alamofire中Response的解读 前言 在每篇文章的前言部分,我都会把我认为的本篇最重要的内容提前讲一下.我更想同大家分享这些顶级框架在设计和编码层次究竟有哪些过人的地方?当然, ...
- Feflow 源码解读
Feflow 源码解读 Feflow(Front-end flow)是腾讯IVWEB团队的前端工程化解决方案,致力于改善多类型项目的开发流程中的规范和非业务相关的问题,可以让开发者将绝大部分精力集中在 ...
最新文章
- AMDP + XLSX Workbench 报表开发模式
- 阿里云服务器(ECS)CentOS修改yum源为阿里源
- 什么是计算机的网络体系结构,什么是网络体系结构 网络体系结构介绍【详解】...
- Python机器学习:KNN算法03训练数据集,测试数据集train test split
- php 全局 路径,PHP问题包括全局路径
- GitBook Editor不能pull新Book时的处理
- Golang网络游戏协议处理框架
- 拓端tecdat|R语言极值理论:希尔HILL统计量尾部指数参数估计可视化
- sqlserver的文件导入到mysql_SQLServer数据导入到MySQL方法介绍
- 7、ESP32-S - 控制 SPI 液晶屏显示图片
- Android - MTK系统源码结构
- 计算机读不出来u盘装系统,电脑重装系统后识别不了U盘解决方法
- 【Java】接口.案例.打印机
- 拉登游戏开发--在时间的积累中寻求美丽图案
- Airbnb创始人:屌丝的逆袭之路
- windows任务栏黑屏不响应?
- 【VOLTE】VOLTE的注册和去注册过程
- Could not set property ‘id‘ of ‘class com.twk.pojo.Book
- macbook pro 卸载mysql_MacBook Pro全新重装OS X Yosemite
- IPB60R099CP-ASEMI代理英飞凌高压MOS管IPB60R099CP
热门文章
- 《AV1 Bitstream Decoding Process Specification》,译名:AV1比特流及解码规范-Chapter 01~02
- 中山证券:从A股两次大牛市的特征 看当前股市走势
- 【Aseprite】像素画背景制作
- Mysql的cpu占用居高不下的解决办法
- docker构建dm8镜像
- C++语言在线代码运行编译工具推荐
- 解决方案| 全民抗“灾”- 实时音视频在应急指挥场景的应用
- 汽车加油问题(贪心)-算法设计与分析
- Python数据结构与算法分析第2版 - 第1章习题(14) - 金钩钓鱼游戏
- CodeMirror显示格式化json