Semaphore 源码解读

news2024/11/16 9:56:05

一、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 个并发。

下面我们通过源码看下 Semaphore 是如何实现的等待唤醒。

二、Semaphore 源码解读

2.1. 对象的创建过程

如果需要一个Semaphore 对象,可以直接通过 new 创建对象,并传入一个信号量的值,那这个值到底给到哪去了呢?

我们可以点到 Semaphore 的构造函数中,构造函数中可以通过 fair 参数控制 Sync 的类型,其实就是控制后面所使用的锁是公平锁还是非公平锁,如果使用的是只有一个 permits 参数的构造函数,则 Sync 的类型是 NonfairSync 也就是非公平锁:

在这里插入图片描述

这里以 NonfairSync 为例,可以点到 NonfairSync 的构造函数中,可以看到信号量值给了父类 Sync 的构造函数:

在这里插入图片描述

Sync 则继承了 AQS ,因此 Semaphore 是依赖于 AQS 阻塞队列的,这里将信号量的大小给到了 AQS 中的 state 变量。

在这里插入图片描述

在这里插入图片描述

2.2. acquire() 获取信号量过程

点到 acquire() 方法中,可以看到又调用了 sync.acquireSharedInterruptibly(1) 也就是 AQS 下的 acquireSharedInterruptibly 方法中:

在这里插入图片描述
在这里插入图片描述

在该方法中首先使用了 tryAcquireShared 方法,可以理解为获取锁的操作,不过不同的是,这里获取的是信号量的剩余,如果剩余小于0了,则表示获取锁失败,需要进行阻塞,下面来看下是如果获取锁的。

这里的 tryAcquireShared 会调用到子类的 tryAcquireShared ,这里以 NonfairSync 为例:

在这里插入图片描述

在这里插入图片描述

下面继续进到 nonfairTryAcquireShared 方法中,这里的逻辑很明了,使用乐观锁自旋的方式,对 state 也就是记录信号量值的变量,进行减去 acquiresacquires在前面就已经看到,写死的 1 ,因此这里将 state 进行了 -1 并赋值,返回的值也就是state - 1 之后的值:

在这里插入图片描述

看到这之后再回到前面的 AQS 下的 acquireSharedInterruptibly 方法中,如果 tryAcquireShared 获取锁成功,也就是信号量余额大于等于 0 ,那这种情况下也无需进行阻塞,但如果小于 0 了此时则表示信号量已经被使用完了,该线程就需要阻塞等待了,下面继续进到 doAcquireSharedInterruptibly 方法中,看是如何实现阻塞等待的:

在这里插入图片描述

进到 doAcquireSharedInterruptibly 方法中,主要使用了 AQS中的几个常用方法,在该方法中首先使用 addWaiter 将当前线程加入到了 AQS 的阻塞队列中:

在这里插入图片描述
在这里插入图片描述

然后使用自旋的方式,找到当前节点的前驱节点,如果前置节点已经是 head 了,那就可以尝试获取锁了,也就是信号量的剩余,如果获取到了锁,也就是信号量大于等于0,则可以唤醒node节点,释放p

在这里插入图片描述

如果前面没有成功获取到锁,下面则需要进行阻塞等待,当被唤醒时,又会接着进行自旋直到获取到锁后结束:
在这里插入图片描述

2.3. release() 释放信号量过程

首先进入到 release() 方法中,可以看到调用了 AQS 下的 releaseShared 方法,并传入了固定值 1
在这里插入图片描述
在这里插入图片描述
这里 tryReleaseShared ,其实也是一种获取锁操作,下面可以进到该方法中看下逻辑:

在这里插入图片描述
在该方法中同样和之前 nonfairTryAcquireShared 方法类似,都是使用乐观锁自旋的方式,不过这里做的是 + releases 也就是 +1,并将 +1 后的值进行替换,并返回 true
在这里插入图片描述
下面再回到 AQS 下的 releaseShared 方法,下面会进入到 doReleaseShared 方法中,该方法则是对已经阻塞的线程进行唤醒:

在这里插入图片描述
在这里插入图片描述

三、总结

阅读了 Semaphore 的源码,可以发现内部基于AQS共享方式实现,因此在并发量大的场景下可以起到较好的性能。

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

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

相关文章

idea初学笔记

注:初学需安装idea专业版&#xff0c;方便学习使用idea运行内存配置从eclipse工具开发 转 idea工具开发&#xff0c;可设置idea快捷键同eclipse快捷键 file -> Settings -> Keymap -> 选择Eclipse -> OK设置idea项目整体编码格式file -> Settings -> Editor …

mysql五种索引类型---实操版本

背景 最近学习了Mysql的索引&#xff0c;索引对于Mysql的高效运行是非常重要的&#xff0c;正确的使用索引可以大大的提高MySql的检索速度。通过索引可以大大的提升查询的速度。不过也会带来一些问题。比如会降低更新表的速度&#xff08;因为不但要把保存数据还要保存一下索引…

Linux【进程理解】

文章目录Linux【进程理解】一、冯诺依曼体系结构二、操作系统OS1.深入理解操作系统2.深入理解系统调用和库函数四、 进程&#xff08;一&#xff09;描述进程-PCB&#xff08;二&#xff09;组织进程和查看进程&#xff08;三&#xff09;通过系统调用创建进程-fork初识&#x…

【python】XML格式文件读写详解

注&#xff1a;最后有面试挑战&#xff0c;看看自己掌握了吗 文章目录XML介绍格式XML与AJAX与HTML区别联系生成XML文件案例用SAX模块处理XML用DOM模块处理XML&#x1f338;I could be bounded in a nutshell and count myself a king of infinite space. 特别鸣谢&#xff1a;…

三:BLE协议架构简介

低功耗蓝牙体系整体架构说明1. PHY(物理层)2. LL(链路层)3. HCI(主机与控制器通信接口)4. L2CAP(逻辑链路控制及适配协议)5. ATT(属性协议)6. GATT(通用属性规范)7. GAP(通用访问规范)8. SM(安全管理)整体架构说明 架构层说明PHY1. 物理层2. 控制射频的发送和接收LL1. 链路层2.…

Java开发 - Quartz初体验

前言 在上一篇博客中&#xff0c;我们对单点登录有了初步了解&#xff0c;这也让我们独立做系统有了最基础的保障。但在业务开发中&#xff0c;总是会出现一些定期处理的任务&#xff0c;我们首先想到的是Timer&#xff0c;但由于其调度功能单一&#xff0c;我们实际并不会用它…

sqlmap工具说明

目录 1 工具简介 3 1.1 简述 3 1.2 背景及需求 3 1.3 主要功能 3 2 功能确认 4 2.1 安装和使用 4 2.1.1 Windows操作系统 4 2.1.2 Linux操作系统 6 2.1.3 Kali 7 2.2 添加目标 7 2.2.1 参数-u 7 2.2.2 参数-m 8 2.2.3 参数-r 9 2.3 指定参数 11 2.4 爆破数据库 11 2.5 爆破表 …

【LeetCode】剑指 Offer(16)

目录 题目&#xff1a;剑指 Offer 33. 二叉搜索树的后序遍历序列 - 力扣&#xff08;Leetcode&#xff09; 题目的接口&#xff1a; 解题思路&#xff1a; 代码&#xff1a; 过啦&#xff01;&#xff01;&#xff01; 写在最后&#xff1a; 题目&#xff1a;剑指 Offer …

Uipath DataTable-Build Data Table(构建数据表)

Build Data Table(构建数据表) 活动描述 Build Data Table(构建数据表)&#xff1a;通过在“构建数据表向导”窗口可以构建数据表。 使用如下图&#xff1a; Build Data Table(构建数据表)属性配置 属性 作用 DisplayName 在设计器面板中设置活动显示的名称 Private 如…

List、Set、Map众多集合框架等你来学,让我们一起精进Java框架的知识点吧

文章目录一、各集合特性二、HashMap深入解析三、遍历集合元素的若干方式一、各集合特性 1. ArrayList特性 不唯一&#xff0c;有序&#xff1a;实现了List接口&#xff0c;该接口是序列&#xff0c;所以不唯一且按顺序保存不同步&#xff1a;因为ArrayList.add&#xff08;&am…

Java 编写Vue组件(VueGWT的初尝试)

在之前&#xff0c;我曾写过这样的文章《不会前端没事&#xff0c;用GWT Boot和Spring Boot构建Web程序》&#xff0c;这篇文字使用的Domino UI来做前端页面&#xff0c;由于现在更流行VUE&#xff0c;并且VUE的页面更具现代化&#xff0c;所以我尝试了一下VueGWT。 VueGWT 有…

Nodejs运行vue项目时,报错:Error: error:0308010C:digital envelope routines::unsupported

前言 前端项目使用( npm run dev ) 运行vue项目时&#xff0c;出现错误&#xff1a;Error: error:0308010C:digital envelope routines::unsupported 经过探索&#xff0c;发现问题所在&#xff0c;主要是nodeJs V17版本发布了OpenSSL3.0对算法和秘钥大小增加了更为严格的限制…

xFormers安装使用

xFormers是一个模块化和可编程的Transformer建模库&#xff0c;可以加速图像的生成。 这种优化仅适用于nvidia gpus&#xff0c;它加快了图像生成&#xff0c;并降低了vram的使用量&#xff0c;而成本产生了非确定性的结果。 下载地址&#xff1a; https://github.com/faceb…

C++ | 你真的了解namespace吗?

文章目录一、前言二、命名冲突三、命名空间1、域作用限定符2、命名空间的概念&#x1f449;示例1&#x1f449;示例23、命名空间的定义4、命名空间的使用① 指定命名空间访问【做项目】② 使用using部分展开【做项目】③ 使用using namespace全局展开【日常练习】5、小结解答&a…

【经验分享】使用了6年的实时操作系统,是时候梳理一下它的知识点了 | 文末赠书4本

使用了6年的实时操作系统&#xff0c;是时候梳理一下它的知识点了 摘要&#xff1a; 本文简单介绍了博主学习操作系统的心路历程&#xff0c;同时还给大家总结了一下当下流行的几种实时操作系统&#xff0c;以及在工程中OSAL应该如何设计。希望对大家有所启发和帮助。 文章目录…

【开放域目标检测】一:Open-Vocabulary Object Detection Using Captions论文讲解

出发点是制定一种更加通用的目标检测问题&#xff0c;目的是借助于大量的image-caption数据来覆盖更多的object concept&#xff0c;使得object detection不再受限于带标注数据的少数类别&#xff0c;从而实现更加泛化的object detection&#xff0c;识别出更多novel的物体类别…

【数据结构】顺序表:尾部操作我很行,随机访问我超快!!!

顺序表的模拟实现 文章目录顺序表的模拟实现1.线性表2.顺序表2.1概念结构2.2顺序表的模拟实现2.2.1顺序表的初始化2.2.2顺序表的销毁2.2.3尾插数据2.2.4尾删数据2.2.5头插数据2.2.6头删数据2.2.7中间插入数据2.2.8中间删除数据2.2.9打印顺序表2.2.10查找数据2.2.11复用Insert和…

Linux学习第二十一节-sudo提权

1.概念 管理员提前为用户设置执行权限许可&#xff1b; 被授权用户有权执行授权命令&#xff1b; 配置文件&#xff1a;/etc/sudoers&#xff1b; 命令格式&#xff1a;sudo 特权命令。 2.提权操作 ①方式一vim编辑配置文件后wq&#xff01;&#xff1a;#vim /etc/sudo…

pnpm 基本详细使用(安装、卸载、使用)

一、简介 官网地址、GitHub地址、官方安装文档、官方卸载文档。 pnpm 全称 performant npm&#xff0c;意思为 高性能的 npm。pnpm 由 npm/yarn 衍生而来&#xff0c;解决了 npm/yarn 内部潜在的 bug&#xff0c;极大的优化了性能&#xff0c;扩展了使用场景。被誉为 最先进的…

【Docker】之docker-compose的介绍与命令的使用

&#x1f341;博主简介 &#x1f3c5;云计算领域优质创作者   &#x1f3c5;华为云开发者社区专家博主   &#x1f3c5;阿里云开发者社区专家博主 &#x1f48a;交流社区&#xff1a;运维交流社区 欢迎大家的加入&#xff01; 文章目录docker-compose简介docker-compose基础…