当前位置: 首页 > news >正文

多线程控制并发数目工具类Semaphore

文章目录

    • 前言
    • Semaphore原理
    • Semaphore源码解析
      • 内部继承AQS保证同步
      • acquire获取许可
      • release释放许可
    • 实战演示
    • 总结

前言

在多线程编码过程中,我们会用到多线程来提升运行效率。比如我们的Executors创建线程池,程序尽可能的压榨CPU资源来提升我们程序吞吐量。但是过度的使用线程,也会将我们CPU资源榨干,从而让我们系统不能正常的提供服务。故今天我们引入JUC并发包下面的semaphore并发类,该类可以同时允许定量线程执行从而达到控制并发的目的。

Semaphore原理

Semaphore并发类提供了两个核心方法:acquire()方法和release()方法。acquire()方法表示获取一个许可,如果没有则等待,release()方法则是释放对应的许可。Semaphore维护了当前访问的个数,通过提供同步机制来控制同时访问的个数。

Semaphore源码解析

内部继承AQS保证同步

Semaphore 与AQS关系图:
在这里插入图片描述

进入java.util.concurrent 包下 Semaphore 并发类查看源码:

//继承AQS同步阻塞队列来实现同步功能
abstract static class Sync extends AbstractQueuedSynchronizer {private static final long serialVersionUID = 1192457210091910933L;Sync(int permits) {setState(permits);}final int getPermits() {return getState();}final int nonfairTryAcquireShared(int acquires) {for (;;) {int available = getState();int remaining = available - acquires;if (remaining < 0 ||compareAndSetState(available, remaining))return remaining;}}

如上所示,首先Semaphore并发内部类继承AbstractQueuedSynchronizer 同步阻塞类,所以可以得出Semaphore是通过AQS机制来保证同步。AQS不明白的同学可以看我之前的博文,大概就是内部一个state状态,获取到资源 state 就加一,释放资源 state 就减一,当 state == 0 的时候表示阻塞队列中的其他线程可以获取该资源。

继续查看源码:

/*** Creates a {@code Semaphore} with the given number of* permits and nonfair fairness setting.** @param permits the initial number of permits available.*        This value may be negative, in which case releases*        must occur before any acquires will be granted.*/
public Semaphore(int permits) {sync = new NonfairSync(permits);
}/*** Creates a {@code Semaphore} with the given number of* permits and the given fairness setting.** @param permits the initial number of permits available.*        This value may be negative, in which case releases*        must occur before any acquires will be granted.* @param fair {@code true} if this semaphore will guarantee*        first-in first-out granting of permits under contention,*        else {@code false}*/
public Semaphore(int permits, boolean fair) {sync = fair ? new FairSync(permits) : new NonfairSync(permits);
}

如源码所示,Semaphore构造方法默认提供非公平锁同步构造方法,也提供了一个用户自定义是否公平锁的构造方法。是否公平锁同步直接影响阻塞队列中的哪个线程可以获取资源,公平锁是按照阻塞顺序获取资源,非公平锁是多个线程争抢资源。当然构造方法必须传入并发许可的总数,这个总数直接影响后续我们可以同时获取多少个许可。

acquire获取许可

查看Semaphore 获取许可的源码:

//是否可以获取许可
public boolean tryAcquire() {return sync.nonfairTryAcquireShared(1) >= 0;
}
//是否可以获取许可,并提供超时获取机制
public boolean tryAcquire(long timeout, TimeUnit unit)throws InterruptedException {return sync.tryAcquireSharedNanos(1, unit.toNanos(timeout));
}
//是否可以获取多个许可,并提供超时获取机制
public boolean tryAcquire(int permits, long timeout, TimeUnit unit)throws InterruptedException {if (permits < 0) throw new IllegalArgumentException();return sync.tryAcquireSharedNanos(permits, unit.toNanos(timeout));
}
//提供一个无参获取许可方法,默认获取一个许可
public void acquire() throws InterruptedException {sync.acquireSharedInterruptibly(1);
}//不间断的获取许可,如果获取不到就阻塞
public void acquireUninterruptibly() {sync.acquireShared(1);
}
//不间断的获取多个许可,如果获取不到就阻塞
public void acquireUninterruptibly(int permits) {if (permits < 0) throw new IllegalArgumentException();sync.acquireShared(permits);
}

如源码所示,我获取许可有很多的方法。有是否可以获取许可,超时是否可以获取许可。当然还有获取许可的方法,以及阻塞获取许可的多个方法,这些方法本质上都是调用父AQS中的获取资源许可的方法,同学们可以选择自己适用的方法进行调用。

release释放许可

查看Semaphore 释放许可的源码:

//默认释放一个许可
public void release() {sync.releaseShared(1);
}
//同时释放多个许可
public void release(int permits) {if (permits < 0) throw new IllegalArgumentException();sync.releaseShared(permits);
}
//调用父AQS的释放资源的方法
public final boolean releaseShared(int arg) {if (tryReleaseShared(arg)) {doReleaseShared();return true;}return false;
}

如源码所示,Semaphore提供了多个释放许可的方法,我们可以根据实际需要选择释放许可的方法。
值得注意的是release() 方法调用了自己内部类Sync的释放资源方法,而Sync又是继承AQS阻塞队列并调用了父类doReleaseShared() 释放资源的方法。

实战演示

上面博文我们介绍了Semaphore并发类是一个同步类,它继承了AQS阻塞队列。Semaphore主要提供了acquire() 获取许可与release() 释放许可的方法,通过这两个方法我们可以实现线程并发数目的功能。下面我们简单模拟一下限制并发数目。

1、Semaphore测试类
测试类提供一个定量5的线程池,用countdownlatch 让主线程等待子线程执行完成,Semaphore保证每次只有3个线程执行。为了保证测试结果可以追溯性,我们在业务逻辑中让线程睡眠3s。

/*** Semaphore test* @author senfel* @date 2023/4/6 11:38 * @return*/
@SpringBootTest
class ConcurrentApplicationTests {/*** 日期格式*/private static DateTimeFormatter formatter = DateTimeFormatter.ofPattern("HH:mm:ss");@Testvoid testSemaphore() throws Exception {//创建线程为5的线程池ExecutorService executorService = Executors.newFixedThreadPool(5);//同时运行3个线程运行Semaphore semaphore = new Semaphore(3);//等待执行完成CountDownLatch countDownLatch = new CountDownLatch(10);for(int i = 0;i<10;i++) {executorService.execute(new Runnable() {@Overridepublic void run() {try{//获取许可semaphore.acquire();//业务逻辑executeFun();//释放许可semaphore.release();countDownLatch.countDown();}catch (Exception e){e.printStackTrace();}}});}countDownLatch.await();System.err.println("执行完成");executorService.shutdown();}/*** 执行业务方法* @author senfel* @date 2023/4/6 11:13* @return void*/private void executeFun() {try {String startTime = formatter.format(LocalDateTime.now());Thread.sleep(3000);System.err.println("当前执行线程:"+Thread.currentThread().getName()+",开始时间:"+startTime+",结束时间"+formatter.format(LocalDateTime.now()));} catch (Exception e) {e.printStackTrace();}}}

2、执行测试方法
执行testSemaphore() 方法,我们可以在控制台看到如下结果:

当前执行线程:pool-1-thread-1,开始时间:11:37:00,结束时间11:37:03
当前执行线程:pool-1-thread-3,开始时间:11:37:00,结束时间11:37:03
当前执行线程:pool-1-thread-2,开始时间:11:37:00,结束时间11:37:03
当前执行线程:pool-1-thread-2,开始时间:11:37:03,结束时间11:37:06
当前执行线程:pool-1-thread-1,开始时间:11:37:03,结束时间11:37:06
当前执行线程:pool-1-thread-3,开始时间:11:37:03,结束时间11:37:06
当前执行线程:pool-1-thread-1,开始时间:11:37:06,结束时间11:37:09
当前执行线程:pool-1-thread-4,开始时间:11:37:06,结束时间11:37:09
当前执行线程:pool-1-thread-2,开始时间:11:37:06,结束时间11:37:09
当前执行线程:pool-1-thread-5,开始时间:11:37:09,结束时间11:37:12
执行完成

根据执行结果我们可以发现每3s有3个线程执行并输出,得出结论Semaphore可以保证每次只能同时3个线程执行!!!!

总结

多线程控制并发数目工具类Semaphore内部继承AQS抽象阻塞队列,并继承了其同步获取、释放资源的方法。Semaphore提供了acquire()获取许可、release()释放许可方法,底层当然调用的是AQS内部方法来满足同步以保证限制并发线程运行数目。

相关文章:

多线程控制并发数目工具类Semaphore

文章目录前言Semaphore原理Semaphore源码解析内部继承AQS保证同步acquire获取许可release释放许可实战演示总结前言 在多线程编码过程中&#xff0c;我们会用到多线程来提升运行效率。比如我们的Executors创建线程池&#xff0c;程序尽可能的压榨CPU资源来提升我们程序吞吐量。…...

Redis篇之五大数据类型

1、五大数据类型 4.1、String&#xff08;字符串&#xff09; String是Redis最基本的类型&#xff0c;你可以理解成与Memcached一模一样的类型&#xff0c;一个key对应一个value String类型是二进制安全的。意味着Redis的string可以包含任何数据。比如jpg图片或者序列化的对象…...

Linux->文件系统磁盘文件管理

目录 1 磁盘结构 2 逻辑抽象管理磁盘 2.1 逻辑抽象 2.2 管理磁盘 2.3 补充知识 3 软硬连接 1 磁盘结构 本篇的学习需要建立在大家在脑海中有一副磁盘的结构才能进行下去&#xff0c;所以我会以图解的方式为大家简单讲解一下&#xff0c;注&#xff1a;博主对这一部分并不是…...

echarts tooltip文字太长换行

tooltip文字太长换行&#xff0c;设置了宽度也没有换行&#xff0c;加上一句&#xff1a; extraCssText: ‘max-width:300px; white-space:pre-wrap’, 没加之前是这样&#xff1a; 加上之后 extraCssText: ‘max-width:300px; white-space:pre-wrap’, tooltip: {trigger: &…...

Docker 部署Jira8.1.0

Jira与Confluence一样&#xff0c;都需要用到独立的数据库&#xff0c;对于数据库的安装我们不做介绍&#xff0c;主要介绍如何用Docker部署Jira以及对Jira进行破解的操作。 1、数据库准备 关于数据库官方文档说明&#xff1a;https://confluence.atlassian.com/adminjiraserv…...

枚举、模拟法(蓝桥杯卡片、数的分解为例)

枚举和模拟算法是计算机领域常用的两种基本算法。枚举算法是一种通过列举所有可能的情况来解决问题的方法。模拟算法则是通过模拟真实场景来解决问题。 枚举、模拟法 枚举算法是指将问题分解为一系列离散的情况&#xff0c;通过枚举所有可能的情况&#xff0c;逐一检查每种情…...

DC-DC升压变换器直流隔离高压输出稳压电源模块5v12v24v48v转50v110v150v220v250v300v350v500v

HRB 系列隔离宽电压输入高电压稳压输出 特点 效率高达 80%以上1*1英寸标准封装单电压输出稳压输出工作温度: -40℃~85℃阻燃封装&#xff0c;满足UL94-V0 要求温度特性好可直接焊在PCB 上应用 HRB 0.2~10W 系列模块电源是一种DC-DC升压变换器。该模块电源的输入电压分为&#…...

jQuery创建、添加、删除元素

一、创建元素 语法&#xff1a; $("<li></li>"); 动态的创建了一个 <li> 二、添加元素 1. 内部添加 1、element.append(内容) 把内容放入匹配元素内部最后面&#xff0c;类似原生 appendChild。 2、element.prepend(内容) 把内容放入匹…...

产品快讯丨神策数据 A/B 测试试验指标管理重磅升级

神策数据&#xff1a;为了更好地帮助企业管理试验指标&#xff0c;神策数据 A/B 测试完善了指标类型、配置方式、计算原理等&#xff0c;帮助分析师、运营同学等明确计算逻辑&#xff0c;并最大程度减少歧义以及与技术同学沟通的成本&#xff0c;以实现企业内部信息的有效统一。…...

游戏开发之Unity2021URP项目场景的构建

地面的修改和编辑&#xff1a;地面插件的使用 打开包管理器&#xff0c;在左边的包那里选择“Unity注册表”&#xff0c;在右边进行搜索“Polybrush”&#xff0c;之后选择右下角的安装 安装完之后要选择样本中的URP进行导入&#xff0c;因为我们的项目是URP渲染管线的&#x…...

数学分析:多元微积分1

卓里奇的数学分析的好处在于直接从多元函数来入手多元微积分&#xff0c;引出矩阵&#xff0c;十分自然。 紧集的概念&#xff0c;感觉直接用闭集去理解就行&#xff0c;&#xff08;对于图形学来说&#xff09;。 多元函数的极限&#xff0c;其实和单元函数并没有什么区别。 这…...

STC32G 三轮车负压电磁

文章目录前言整车效果控制思路循迹环岛处理障碍处理关键代码部分差比和以及当前速度计算角速度环速度环环岛处理障碍处理前言 年后就没怎么碰车了&#xff0c;到3月中旬换三轮了&#xff0c;可算有一点成效了&#xff0c;做个记录。 整车效果 三轮负压电磁慢速元素识别控制思…...

【编程小记】位运算 x -x 表示含义

位运算 x & -x 表示含义一、原码反码补码二、位运算 x & -x 表示含义三、最终结论一、原码反码补码 在计算机中&#xff0c;整数的数据的存储是按照补码的方式进行存储的 按照数据与0的大小&#xff0c;数据又被分为正数与负数 正数的原码反码补码相同。负数的原码&…...

信创PC利旧管理新模式,麒麟信安助力国家某部委实现高效云办公

2022年&#xff0c;国家某部委所有桌面终端均已完成信创PC替换&#xff0c;并将日常办公所需的办公Office套件、OA无纸化办公系统、即时通讯系统等全部迁移至信创PC&#xff0c;但在进行生产业务系统迁移时&#xff0c;该单位信创PC仍存在业务系统与不同芯片PC难适配、应用难兼…...

【玩转RT-Thread】RT-Thread内核宏定义详解(rtdef.h)

文章目录1.RT-Thread版本信息2.RT-Thrad基础数据类型定义3.RT-Thread基本数据类型的范围4.RT-Thread系统滴答时钟最大计数值5.RT-Thread IPC数据类型范围6.RT-Thread避免未使用变量警告7.编译器相关定义8.编译器相关定义9.RT-Thread错误码定义1.RT-Thread版本信息 /* RT-Threa…...

PDF转化器免费版有哪些?这几款办公达人们都在用

在现代办公中&#xff0c;文件的排版和格式是非常重要的&#xff0c;无论是发布通知或提交策划书、投档简历或是发表论文、宣传海报或是产品说明书等&#xff0c;我们经常使用PDF文件格式发送给他人。然而&#xff0c;很多人需要对PDF进行编辑修改&#xff0c;通常会先将其转换…...

2022MathorCup赛题B

以下所有文字均基于作者的实际经验&#xff0c;并不具有完全的合理性&#xff0c;请谨慎参考 目录 一、问题分析 &#xff08;一&#xff09;问题一 &#xff08;二&#xff09;问题二 二、预处理 &#xff08;一&#xff09;训练集预处理 &#xff08;二&#xff09;测…...

适合销售使用的CRM系统特点

销售人员抱怨CRM系统太复杂&#xff0c;这是一个很重要的问题。毕竟&#xff0c;如果系统太难使用&#xff0c;会导致CRM实用率和效率下降&#xff0c;最终影响公司的运作。在这篇文章中&#xff0c;我们来探讨当销售抱怨crm客户系统太复杂了&#xff0c;企业该如何解决。 缺少…...

项目中获取resource下文件路径的方法

String filepathrequest.getServletContext().getRealPath("/")"files\\"; 获取的当前文件在实际运行的tomcat地址目录 String path ClassUtils.getDefaultClassLoader().getResource("").getPath()"tmp/files/"; 获取的是当前文件…...

Air32F103CBT6|CCT6|KEIL-uVsion5|本地编译|STClink|(6)、Air32F103编译下载

目录 一、环境搭建 准备工作 安装支持包 二、新建工程 添加外设库支持 测试代码 三、下载烧录 一、环境搭建 准备工作 安装MDK5&#xff0c;具体方法请百度&#xff0c;安装后需要激活才能编译大文件 下载安装AIR32F103的SDK&#xff1a;luatos-soc-air32f103: Air32f…...

51c自动驾驶~合集58

我自己的原文哦~ https://blog.51cto.com/whaosoft/13967107 #CCA-Attention 全局池化局部保留&#xff0c;CCA-Attention为LLM长文本建模带来突破性进展 琶洲实验室、华南理工大学联合推出关键上下文感知注意力机制&#xff08;CCA-Attention&#xff09;&#xff0c;…...

React Native在HarmonyOS 5.0阅读类应用开发中的实践

一、技术选型背景 随着HarmonyOS 5.0对Web兼容层的增强&#xff0c;React Native作为跨平台框架可通过重新编译ArkTS组件实现85%以上的代码复用率。阅读类应用具有UI复杂度低、数据流清晰的特点。 二、核心实现方案 1. 环境配置 &#xff08;1&#xff09;使用React Native…...

【git】把本地更改提交远程新分支feature_g

创建并切换新分支 git checkout -b feature_g 添加并提交更改 git add . git commit -m “实现图片上传功能” 推送到远程 git push -u origin feature_g...

代码随想录刷题day30

1、零钱兑换II 给你一个整数数组 coins 表示不同面额的硬币&#xff0c;另给一个整数 amount 表示总金额。 请你计算并返回可以凑成总金额的硬币组合数。如果任何硬币组合都无法凑出总金额&#xff0c;返回 0 。 假设每一种面额的硬币有无限个。 题目数据保证结果符合 32 位带…...

return this;返回的是谁

一个审批系统的示例来演示责任链模式的实现。假设公司需要处理不同金额的采购申请&#xff0c;不同级别的经理有不同的审批权限&#xff1a; // 抽象处理者&#xff1a;审批者 abstract class Approver {protected Approver successor; // 下一个处理者// 设置下一个处理者pub…...

LangChain知识库管理后端接口:数据库操作详解—— 构建本地知识库系统的基础《二》

这段 Python 代码是一个完整的 知识库数据库操作模块&#xff0c;用于对本地知识库系统中的知识库进行增删改查&#xff08;CRUD&#xff09;操作。它基于 SQLAlchemy ORM 框架 和一个自定义的装饰器 with_session 实现数据库会话管理。 &#x1f4d8; 一、整体功能概述 该模块…...

腾讯云V3签名

想要接入腾讯云的Api&#xff0c;必然先按其文档计算出所要求的签名。 之前也调用过腾讯云的接口&#xff0c;但总是卡在签名这一步&#xff0c;最后放弃选择SDK&#xff0c;这次终于自己代码实现。 可能腾讯云翻新了接口文档&#xff0c;现在阅读起来&#xff0c;清晰了很多&…...

CRMEB 中 PHP 短信扩展开发:涵盖一号通、阿里云、腾讯云、创蓝

目前已有一号通短信、阿里云短信、腾讯云短信扩展 扩展入口文件 文件目录 crmeb\services\sms\Sms.php 默认驱动类型为&#xff1a;一号通 namespace crmeb\services\sms;use crmeb\basic\BaseManager; use crmeb\services\AccessTokenServeService; use crmeb\services\sms\…...

使用SSE解决获取状态不一致问题

使用SSE解决获取状态不一致问题 1. 问题描述2. SSE介绍2.1 SSE 的工作原理2.2 SSE 的事件格式规范2.3 SSE与其他技术对比2.4 SSE 的优缺点 3. 实战代码 1. 问题描述 目前做的一个功能是上传多个文件&#xff0c;这个上传文件是整体功能的一部分&#xff0c;文件在上传的过程中…...

如何在Windows本机安装Python并确保与Python.NET兼容

✅作者简介&#xff1a;2022年博客新星 第八。热爱国学的Java后端开发者&#xff0c;修心和技术同步精进。 &#x1f34e;个人主页&#xff1a;Java Fans的博客 &#x1f34a;个人信条&#xff1a;不迁怒&#xff0c;不贰过。小知识&#xff0c;大智慧。 &#x1f49e;当前专栏…...