【源码解析】Semaphore角度聊聊AQS

案例

Semaphore,俗称信号量,它是操作系统中PV操作的原语在java的实现,它也是基于 AbstractQueuedSynchronizer实现的

private static ExecutorService threadPool = Executors.newFixedThreadPool(4);private static Semaphore semaphore = new Semaphore(2);public static void main(String[] args) {for ( ; ; ) {threadPool.execute(()-> exec());}}public static void exec() {try {semaphore.acquire();System.out.println(Thread.currentThread().getName() + " before");TimeUnit.SECONDS.sleep(1);System.out.println("执行任务");System.out.println(Thread.currentThread().getName() + " after");} catch (InterruptedException e) {e.printStackTrace();} finally {semaphore.release();}}

从执行结果来看的话,先是2个线程获取到凭证,然后执行完毕。后续两个线程才开始获取凭证。

pool-1-thread-1 before
pool-1-thread-2 before
执行任务
执行任务
pool-1-thread-2 after
pool-1-thread-1 after
pool-1-thread-3 before
pool-1-thread-4 before
执行任务
执行任务
pool-1-thread-3 after
pool-1-thread-4 after

应用场景

Semaphore的使用场景主要用于一些中间件的时候,进行限流使用。

源码解析

构造方法

默认是非公平锁,可以通过构造参数进行设置。本篇主要介绍非公平锁的实现方式。

    public Semaphore(int permits) {sync = new NonfairSync(permits);}public Semaphore(int permits, boolean fair) {sync = fair ? new FairSync(permits) : new NonfairSync(permits);}
    // 非公平锁static final class NonfairSync extends Sync {private static final long serialVersionUID = -2694183684443567898L;NonfairSync(int permits) {super(permits);}protected int tryAcquireShared(int acquires) {return nonfairTryAcquireShared(acquires);}}
        Sync(int permits) {setState(permits);}// 设置state为构造方法的数值protected final void setState(int newState) {state = newState;}

获取凭证

            semaphore.acquire();
    public void acquire() throws InterruptedException {sync.acquireSharedInterruptibly(1);}
    public final void acquireSharedInterruptibly(int arg)throws InterruptedException {//是否中断if (Thread.interrupted())throw new InterruptedException();// 线程等待if (tryAcquireShared(arg) < 0)doAcquireSharedInterruptibly(arg);}

T1线程直接获取锁,返回。T2线程也可以获取,但是T3线程进入的时候 state=0,获取不到锁。就会进入到 doAcquireSharedInterruptibly 这个逻辑中

        final int nonfairTryAcquireShared(int acquires) {for (;;) {获取当前state的值int available = getState();int remaining = available - acquires;if (remaining < 0 ||// //cas操作compareAndSetState(available, remaining))return remaining;}}

doAcquireSharedInterruptibly 其实就是将当前线程封装成一个Node节点,添加到AQS队列中。 shouldParkAfterFailedAcquire 会进行阻塞。

    private void doAcquireSharedInterruptibly(int arg)throws InterruptedException {// 封装成一个node 加入AQS队列中 共享模式final Node node = addWaiter(Node.SHARED);boolean failed = true;try {//自选锁for (;;) {final Node p = node.predecessor();if (p == head) {// state 不等于0 返回-1int r = tryAcquireShared(arg);// 第一次不会进入if (r >= 0) {//  // 2. 这里将唤醒t3的后续节点t4,以此类推,t4被唤醒后,会在t4的await中唤醒t4的后续节点setHeadAndPropagate(node, r);// t3节点删除p.next = null; // help GCfailed = false;return;}}// 修改前驱节点waitstate = -1 挂起当前线程if (shouldParkAfterFailedAcquire(p, node) &&parkAndCheckInterrupt())throw new InterruptedException();}} finally {if (failed)cancelAcquire(node);}}

释放凭证

    public void release() {sync.releaseShared(1);}
    public final boolean releaseShared(int arg) {if (tryReleaseShared(arg)) {doReleaseShared();return true;}return false;}

获取当前state的值,然后将state+=1 操作。

        protected final boolean tryReleaseShared(int releases) {for (;;) {int current = getState();int next = current + releases;if (next < current) // overflowthrow new Error("Maximum permit count exceeded");if (compareAndSetState(current, next))return true;}}

释放资源。 unparkSuccessor(h); 会将T3线程进行唤醒。然后T3线程会尝试唤醒T4 (共享模式)。如果有资源的话,就获取锁,没有的话就会阻塞。

    private void doReleaseShared() {// 自选锁for (;;) {Node h = head;if (h != null && h != tail) {int ws = h.waitStatus;// 前面已经将pre节点 设置为-1if (ws == Node.SIGNAL) {// 设置为0 if (!compareAndSetWaitStatus(h, Node.SIGNAL, 0))continue;            // loop to recheck cases// 唤醒head的后继节点unparkSuccessor(h);}else if (ws == 0 &&!compareAndSetWaitStatus(h, 0, Node.PROPAGATE))continue;                // loop on failed CAS}if (h == head)                   // loop if head changedbreak;}}

小结

通过代码进一步分析 可以更加了解Semaphore的原理。
在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/231115.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

python学习4

大家好&#xff0c;这里是七七&#xff0c;今天带来的例子是双对数模型回归代码.总的代码会在文章最后处 本文的风格依旧是面向新手的基础性介绍内容。 目录 库介绍 一、numpy库 二、scipy库 三、sklearn库 代码1 代码2 代码3 代码4 代码五 总代码 库介绍 一、num…

Python函数装饰器基础举例

1 Python函数装饰器基础举例 1.1 用staticmethod统计实例数 描述 staticmethod后接def funcname&#xff0c; 效果等于 funcnamestaticmethod(funcname)&#xff0c;只是书写简洁一些。 示例 >>> class CountInsBISM:numOfInstances0def __init__(self):CountIn…

设计模式——结构型

1.装饰器模式 要素&#xff1a;装饰器&#xff0c;装饰对象 为待装饰对象中某一结构特征添加内容&#xff0c;而不是新建一个特征 /*** 装饰对象*/ public interface Shape {public void draw(); } /*** 具体装饰对象*/ public class Circle implements Shape{private String…

计算机组成原理——校验码

计算机组成原理学习笔记——校验码-CSDN博客 校验码——海明码及码距&#xff0c;码距_海明码的码距是多少-CSDN博客 1 下列关于码距与检错与纠错能力的描述中正确的是 &#xff08;ABC&#xff09; &#xff08;多选&#xff09; A. 码距为1的编码不具备任何检错能力 B. 码…

社交网络分析4(上):社交网络链路预测分析、Logistic回归模型、LLSLP方法(LightGBM 堆叠链路预测)、正则化方法、多重共线性

社交网络分析4 写在最前面社交网络链路预测分析概述链路预测分析简介链路预测分析的重要性社交网络链路预测分析方法基于网络结构的方法基于节点属性的方法基于随机游走的方法基于深度学习的方法 基于相似性和基于似然性的链路预测方法基于相似性的方法基于邻居的方法基于路径的…

Linux shell编程学习笔记36:read命令

*更新日志 *2023-12-18 1.根据[美] 威廉肖特斯 &#xff08;Willian shotts&#xff09;所著《Linux命令行大全&#xff08;第2版&#xff09;》 更新了-e、-i、-r选项的说明 2.更新了 2.8 的实例&#xff0c;增加了gif动图 3.补充了-i的应用实例 2.1…

回归预测 | MATLAB实现SABO-LSTM基于减法平均优化器优化长短期记忆神经网络的多输入单输出数据回归预测模型 (多指标,多图)

回归预测 | MATLAB实现SABO-LSTM基于减法平均优化器优化长短期记忆神经网络的多输入单输出数据回归预测模型 &#xff08;多指标&#xff0c;多图&#xff09; 目录 回归预测 | MATLAB实现SABO-LSTM基于减法平均优化器优化长短期记忆神经网络的多输入单输出数据回归预测模型 &a…

01矩阵(课程F)

输入2个整数N和M&#xff0c;输出N行M列的的01数字矩阵。第1行第1个数是’1’&#xff0c;后面的数都和其左边或上面数相反&#xff08;0和1称为相反&#xff09;。例如&#xff1a;N4,M5时&#xff1a; 10101 01010 10101 01010 输入格式 第一行2个正整数&#xff1a;N和M&a…

【mysql】mysql的索引有哪些?聚簇索引和非聚簇索引又是个啥?

0 回答 聚簇索引 将数据存储与索引放到了一块&#xff0c;找到了索引也就找到了数据&#xff0c;当表有聚簇索引时&#xff0c;它的数据实际上存放在索引的叶子页上&#xff0c;也就是B树的叶子节点上&#xff0c;因为数据行不能存在两个地方&#xff0c;所以一个表只能有一个聚…

自清洗过滤器工作原理尺寸选型参数,内部结构,压差开关如何调节

​ 1&#xff1a;全自动自清洗过滤器设备介绍 全自动反冲洗过滤器是水净化过程中不可缺少的处理手段&#xff0c;用于拦截水中的各种杂质&#xff0c;以净化水质或保护系统中其他设备的正常工作。普通网式过滤器因其结构简单、过滤效果好、阻力小而广泛应用于水源过滤、工业循…

[RK-Linux] RK3399使用RK开源SPL,修改U-Boot为FIT打包方式,裁剪trust分区

文章目录 一、启动方式二、FIT打包三、RK3568相关配置参考四、RK3399支持与调试一、启动方式 RK3399平台根据前级Loader代码是否开源,目前有两套启动方式: // 前级loader闭源 BOOTROM => ddr bin => Miniloader => TRUST => U-BOOT => KERNEL // 前级loader…

神经网络可以计算任何函数的可视化证明

神经网络可以计算任何函数的可视化证明 对于神经网络&#xff0c;一个显著的事实就是它可以计算任何函数。 如下&#xff1a;不管该函数如何&#xff0c;总有神经网络能够对任何可能的输入x&#xff0c;输出值f&#xff08;x&#xff09; 即使函数有很多输入和输出&#xff0…

货物数据处理,pandas和openpyxl联合处理

处理文件题头格式 部门名称 年度名称 季节名称 商品名称 商品代码 品牌名称 品类名称 颜色名称 商店名称 0M 1L 1XL 27 28 29 2XL 30 31 32 33 3XL 4XL 5XL 6XL S 均1.导入包 导入源 pip install openpyxl -i https://pypi.doubanio.com/simple pip install pandas -i https…

Git提交前的必备神器——自动清除调试语句脚本

说在前面 不知道大家有没有遇到这样一种情况&#xff0c;平时在写代码调试时有时候会使用到debugger&#xff0c;可能大部分时间在提交代码前会记得把debugger先删除&#xff0c;但可能也会存在将debugger提交上去的情况&#xff0c;那我们该怎么防止出现这种情况呢&#xff1f…

RLC防孤岛负载测试:电力系统安全运行的重要保障

在电力系统中&#xff0c;孤岛效应是一个严重的问题&#xff0c;它可能导致电力系统的不稳定甚至崩溃。为了确保电力系统的安全运行&#xff0c;必须进行RLC防孤岛负载测试。RLC防孤岛负载测试是一种模拟电网故障后&#xff0c;对电力系统进行检测的方法&#xff0c;主要用于检…

在 linux上运行 Scratch,找到了更github 的项目地址,而且找到了scratch的官方项目。

1&#xff0c;关于Scratch Scratch 是麻省理工学院的“终身幼儿园团队”发布的一种图形化编程工具&#xff0c; 主要面对全球青少年开放&#xff0c;所有人都可以在软件中创作自己的程序。 2&#xff0c;在linux 上面还真有个默认的 scratch 版本 但是太老旧了。 于是找了下…

天猫数据分析平台-天猫销售数据查询软件-11月天猫平台冲锋衣市场销售运营数据分析

随着气温逐渐下降&#xff0c;保暖服饰迎来热销&#xff0c;冲锋衣的需求大增。如今冲锋衣已经不仅仅是户外运动的装备&#xff0c;还成为很多年轻人的日常穿搭和时尚的追求。 新的穿搭趋势也带来了巨大的市场机会。据公开数据显示&#xff0c;中国有冲锋衣生产及经营企业超过8…

分布式事务--TC服务的高可用和异地容灾

1.模拟异地容灾的TC集群 计划启动两台seata的tc服务节点&#xff1a; 节点名称ip地址端口号集群名称seata127.0.0.18091SHseata2127.0.0.18092HZ 之前我们已经启动了一台seata服务&#xff0c;端口是8091&#xff0c;集群名为SH。 现在&#xff0c;将seata目录复制一份&…

消息队列-broker

在消息队列系统中&#xff0c;Broker&#xff08;代理&#xff09;是指消息队列的中间件服务器&#xff0c;负责接收、存储和转发消息。它充当了消息发送者和接收者之间的中间人&#xff0c;协调消息的传递和处理。 具体来说&#xff0c;Broker在消息队列中扮演以下角色&#…

9. DashBoard

9. DashBoard 文章目录 9. DashBoard9.1 部署Dashboard9.2 使用DashBoard 在kubernetes中完成的所有操作都是通过命令行工具kubectl完成的。 为了提供更丰富的用户体验&#xff0c;kubernetes还开发了一个基于web的用户界面&#xff08;Dashboard&#xff09;。 用户可以使用…