Java并发编程三大神器之Semaphore

Java并发编程三大神器之Semaphore

  • 1、Semaphore是什么
  • 2、Semaphore小试牛刀
  • 3、Semaphore和CountDownLatch组合使用
  • 4、Semaphore常用方法
  • 5、Semaphore 结语

1、Semaphore是什么

Semaphore 是一个计数信号量,是JDK1.5引入的一个并发工具类,位于java.util.concurrent包中。可以控制同时访问资源的线程个数。Semaphore机制是提供给线程抢占式获取许可,所以他可以实现公平或者非公平,类似于ReentrantLock。

《Java并发编程艺术》中说:Semaphore(信号量)是用来控制同时访问特定资源的线程数量,它通过协调各个线程,以保证合理的使用公共资源。

举个栗子:我们去停车场停车,停车场总共只有5个车位,但是现在有8辆汽车来停车,剩下的3辆汽车要么等其他汽车开走后进行停车,要么去其他停车场找别的停车位。

举个栗子:公园里面的厕所只有4个坑,假如有10个人要上厕所的话,那么同时能去上厕所的人就只能有4个,还剩下6个人只能在外面等待。当4个人中有任何一个人离开后,其中在等待的人中才有一个人可以继续使用,依次下去,直到所有人都上完厕所。

2、Semaphore小试牛刀

Semphore计数信号量由 new Semaphore(N) 指定数量N的 “许可” 初始化。每调用一次 acquire(),一个许可会被调用线程取走。每调用一次 release(),一个许可会被返还给信号量。因此,在没有任何 release() 调用时,最多有 N 个线程能够通过 acquire() 方法,N是该信号量初始化时的许可的指定数量。这些许可只是一个简单的计数器。

如下的例子,有一个商场拥有4个车位的停车场,有10辆车需要停车,当停进4辆车后,其余的6辆车就需要等待已经停进去的车开出去,才可以再停进去。

public class ParkingLot extends Thread {// 信号量private Semaphore semaphore;// 同时允许多少个线程同时执行private int num;public ParkingLot(Semaphore semaphore, int num) {this.semaphore = semaphore;this.num = num;}@Overridepublic void run() {try {//汽车驶入停车场,需要获取一个许可semaphore.acquire();System.out.println(LocalDateTime.now() + " 第" + this.num + "辆汽车驶入停车场...");Thread.sleep(2000);System.out.println(LocalDateTime.now() + " 第" + this.num + "辆汽车驶出停车场>>>");//汽车驶出停车场,会释放一个许可semaphore.release();} catch (InterruptedException e) {e.printStackTrace();}}
}
public class ParkingLotSemaphoreDemo {// 停车场总共有5个车位private static final int MAX_AVAILABLE = 4;public static void main(String[] args) {//只有5个车位,就是说同时只能允许5辆汽车进行停车Semaphore semaphore = new Semaphore(MAX_AVAILABLE);//模拟10辆车驶入停车场for (int i = 1; i <= 10; i++) {new ParkingLot(semaphore, i).start();}}
}

在这里插入图片描述

3、Semaphore和CountDownLatch组合使用

比如说

public class SemaphoreTest {public static void main(String[] args) throws InterruptedException {// 初始化五个车位Semaphore semaphore = new Semaphore(5);// 等所有车子final CountDownLatch latch = new CountDownLatch(8);for (int i = 1; i <= 8; i++) {int finalI = i;if (i == 5) {Thread.sleep(1000);new Thread(() -> {stopCarNotWait(semaphore, finalI);latch.countDown();}).start();continue;}new Thread(() -> {stopCarWait(semaphore, finalI);latch.countDown();}).start();}latch.await();log("总共还剩:" + semaphore.availablePermits() + "个车位");}// 停车 等待车位private static void stopCarWait(Semaphore semaphore, int finalI) {String format = String.format("车牌号%d", finalI);try {semaphore.acquire(1);log(format + "找到车位了,去停车了");Thread.sleep(10000);} catch (Exception e) {e.printStackTrace();} finally {semaphore.release(1);log(format + "开走了");}}// 不等待车位 去其他地方停车 private static void stopCarNotWait(Semaphore semaphore, int finalI) {String format = String.format("车牌号%d", finalI);try {if (semaphore.tryAcquire()) {log(format + "找到车位了,去停车了");Thread.sleep(10000);log(format + "开走了");semaphore.release();} else {log(format + "没有停车位了,不在这里等了去其他地方停车去了");}} catch (Exception e) {e.printStackTrace();}}// 日志public static void log(String content) {// 格式化DateTimeFormatter fmTime = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");// 当前时间LocalDateTime now = LocalDateTime.now();System.out.println(now.format(fmTime) + "  " + content);}
}

在这里插入图片描述

从输出结果可以看到车牌号6这辆车一看没有车位了,就不在这个地方傻傻的等了,而是去其他地方了,但是车牌号7车牌号8分别需要等到车库开出2辆车空出2个车位后才停进去。这就体现了 Semaphoreacquire() 方法如果没有获取到凭证它就会阻塞,而 tryAcquire() 方法如果没有获取到凭证不会阻塞的。

4、Semaphore常用方法

public Semaphore(int permits):创建一个信号量,参数permits表示许可数目,即同时可以允许多少线程进行访问。默认采用的是非公平的策略。
public Semaphore(int permits, boolean fair):创建一个信号量,参数permits表示许可数目,即同时可以允许多少线程进行访问。多了一个参数fair表示是否是公平的,即等待时间越久的越先获取许可。

public void acquire():用于获取一个许可,若无许可能够获得,则会一直等待,直到获得许可。【会阻塞】
public void acquire(int permits):用于获取permits个许可,若无许可能够获得,则会一直等待,直到获得许可。【会阻塞】

public void release():用于释放一个许可。在释放许可之前,得先获得许可。【会阻塞】
public void release(int permits):用于释放permits个许可。在释放许可之前,得先获得许可。【会阻塞】

public boolean tryAcquire():尝试获取一个许可,若获取成功,则立即返回true,若获取失败,则立即返回false。【不会阻塞】
public boolean tryAcquire(long timeout, TimeUnit unit):尝试获取一个许可,若在指定的时间内获取成功,则立即返回true,否则则立即返回false。【不会阻塞】
public boolean tryAcquire(int permits):尝试获取permits个许可,若获取成功,则立即返回true,若获取失败,则立即返回false。【不会阻塞】
public boolean tryAcquire(int permits, long timeout, TimeUnit unit):尝试获取permits个许可,若在指定的时间内获取成功,则立即返回true,否则则立即返回false。【不会阻塞】

public int availablePermits():用于获取信号量中当前可用的许可数目。

5、Semaphore 结语

  • 当信号量Semaphore初始化设置许可证为 1 时,它也可以当作互斥锁使用。其中 0、1 就相当于它的状态,当=1时 表示其他线程可以获取;当=0时 排他,即其他线程必须要等待。
  • Semaphore是JUC包中的一个很简单的工具类,用来实现多线程下对于资源的同一时刻的访问线程数限制
  • Semaphore中存在一个【许可】的概念,即访问资源之前,先要获得许可,如果当前许可数量为0,那么线程阻塞,直到获得许可。
  • Semaphore内部使用AQS实现,由抽象内部类Sync继承了AQS。因为Semaphore天生就是共享的场景,所以其内部实际上类似于共享锁的实现。
  • 共享锁的调用框架和独占锁很相似,它们最大的不同在于获取锁的逻辑——共享锁可以被多个线程同时持有,而独占锁同一时刻只能被一个线程持有。
  • 由于共享锁同一时刻可以被多个线程持有,因此当头节点获取到共享锁时,可以立即唤醒后继节点来争锁,而不必等到释放锁的时候。因此,共享锁触发唤醒后继节点的行为可能有两处,一处在当前节点成功获得共享锁后,一处在当前节点释放共享锁后。
  • 采用semaphore来进行限流的话会产生突刺现象。【突刺现象:指在一定时间内的一小段时间内就用完了所有资源,后大部分时间中无资源可用。比如在限流方法中的计算器算法,设置1s内的最大请求数为100,在前100ms已经有了100个请求,则后面900ms将无法处理请求。

参考:https://cloud.tencent.com/developer/article/1801558

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

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

相关文章

logTrick

贴一下灵神的题解里面的解释~ 就是一种优化策略&#xff0c;logtrick class Solution { public:int minimumDifference(vector<int>& nums, int k) {int res 0x3f3f3f3f;int n nums.size();for(int i0;i<n;i){res min(res,abs(nums[i]-k));for(int j i-1;j&g…

算法题解记录28+++对称二叉树(百日筑基)

一、题目描述&#xff1a; 题目难度&#xff1a;简单 给你一个二叉树的根节点 root &#xff0c; 检查它是否轴对称。 示例 1&#xff1a; 输入&#xff1a;root [1,2,2,3,4,4,3] 输出&#xff1a;true 分割线 示例 2&#xff1a; 输入&#xff1a;root [1,2,2,null,3,nul…

SpringAI学习及搭建AI原生应用

文章目录 一、SpringAI是什么二、准备工作1.GPT-API-free2.AiCore3.eylink 三、对话案例实现1.创建项目2.实现简单的对话 四、聊天客户端ChatClient1.角色预设2.流式响应3.call和stream的区别 五、聊天模型提示词提示词模板 六、图像模型(文生图)七、语音模型1.文字转语音(文生…

【C语言】分支与循环(下)

目录 6. while 循环 7. for 循环 8. do-while 循环 9. break 和 continue 循环 10. 循环的嵌套 11. goto 语句 正文开始—— 6. while 循环 C语言提供了三种循环&#xff0c;while 就是其中一种。 while 语句的语法结构与 if 语句非常相似。 6.1 if 和 wh…

ubuntu中安装docker并换源

使用 Ubuntu 的仓库安装 Docker sudo apt update现在&#xff0c;你可以使用以下命令在 Ubuntu 中安装 Docker 以及 Docker Compose&#xff1a; sudo apt install docker.io docker-composeDocker 包被命名为 docker.io&#xff0c;因为在 Docker 出现之前就已经存在一个名为…

【论文复现|智能算法改进】基于多策略融合灰狼算法的移动机器人路径规划

目录 1.算法原理2.改进点3.结果展示4.参考文献5.代码获取 1.算法原理 【智能算法】灰狼算法&#xff08;GWO&#xff09;原理及实现 2.改进点 混沌反向学习策略 融合Logistic混沌映射和Tent混沌映射生成Logistic-Tent复合混沌映射: Z i 1 { ( r Z i ( 1 − Z i ) ( 4 −…

window11 系统更新失败处理办法

方法一&#xff1a;运行 Windows 更新疑难解答 按 Win I 打开设置。选择“系统”。选择“疑难解答”&#xff0c;然后点击“其他疑难解答”。找到“Windows 更新”&#xff0c;并运行疑难解答。 方法二&#xff1a;使用 DISM 工具修复系统文件 在开始菜单中搜索“命令提示符…

618大促背后的智能力量:天润融通如何用AI大模型提升客户服务?

五一结束之后&#xff0c;消费零售企业马上又要进入一场紧锣密鼓的新战斗——618&#xff0c;一场上半年最重要的促销活动。 对品牌和商家来说&#xff0c;每年618都是一场新考验。因为618时间有限&#xff0c;而消费趋势总是在不断变化&#xff0c;市场竞争又越来越激烈。如何…

掌握高等数学、线性代数、概率论所需数学知识及标题建议

在数学的广袤领域中&#xff0c;高等数学、线性代数和概率论作为三大核心分支&#xff0c;不仅在理论研究中占据重要地位&#xff0c;更在实际应用中发挥着举足轻重的作用。为了深入理解和掌握这三门学科&#xff0c;我们需要掌握一系列扎实的数学知识。 高等数学所需数学知识 …

Tensorflow-GPU工具包了解和详细安装方法

目录 基础知识信息了解 显卡算力 CUDA兼容 Tensorflow gpu安装 CUDA/cuDNN匹配和下载 查看Conda driver的版本 下载CUDA工具包 查看对应cuDNN版本 下载cuDNN加速库 CUDA/cuDNN安装 CUDA安装方法 cuDNN加速库安装 配置CUDA/cuDNN环境变量 配置环境变量 核验是否安…

算法与数据结构--决策树算法

欢迎来到 Papicatch的博客 文章目录 &#x1f349;决策树算法介绍 &#x1f348;原理 &#x1f348;核心思想包括 &#x1f34d;递归分割 &#x1f34d;选择标准 &#x1f34d;剪枝 &#x1f348;解题过程 &#x1f34d;数据准备 &#x1f34d;选择最佳分割特征 &…

【GD32】 TIMER通用定时器学习+PWM输出占空比控制LED

扩展&#xff1a;对PWM波形的输出进行捕获 目录 一、简介二、具体功能描述1、时钟源的选择&#xff1a;2、预分频器&#xff1a;3、计数模式&#xff1a;向上计数模式&#xff1a;向下计数模式&#xff1a;中央对齐模式&#xff1a; 4、捕获/比较通道 输入捕获模式 输出比…

仙侠手游【天道情缘】修复版服务端+GM后台+详细教程

下载地址&#xff1a;仙侠手游【天道情缘】修复版服务端GM后台详细教程

【电子信息工程专业课】学习记录

数字信号处理 离散时间信号与系统 周期延拓 一个连续时间信号经过理想采样后&#xff0c;其频谱将沿着频率轴以采样频率Ωs 2π / T 为间隔而重复。 混频 各周期的延拓分量产生频谱交替的现象 奈奎斯特采样定理 fs > 2fh Z变换 收敛域&#xff1a;使任意给定序列x(n)的Z变…

ROS机器人小车建模仿真与SLAM

文章目录 一、URDF二、创建小车模型1.创建功能包2.导入依赖3.创建urdf,launch文件&#xff1a;4.可视化 三、添加雷达1.xacro文件2.集成和修改launch3.添加摄像头和雷达 三.GAZEBO仿真四、orbslam2kitti1.下载2.安装编译ORB_SLAM23.运行Kitee数据集 一、URDF ​ URDF&#xff…

传输层——TCP

在学习计算机网络的过程中&#xff0c;我们知道OSI七层协议模型&#xff0c;但是在实际开发应 用中我们发现OSI七层协议模型并不适合实施&#xff0c;因为OSI上三层通常都是由开 发人员统一完成的&#xff0c;这三层之间在实现过程中没有一个明确的界限&#xff0c;所以我 们更…

爬虫相关面试题

一&#xff0c;如何抓取一个网站&#xff1f; 1&#xff0c;去百度和谷歌搜一下这个网站有没有分享要爬取数据的API 2, 看看电脑网页有没有所需要的数据&#xff0c;写代码测试调查好不好拿&#xff0c;如果好拿直接开始爬取 3&#xff0c;看看有没有电脑能打开的手机网页&a…

面向对象初级的内存分布图

1.一个对象的内存图 2.二个对象的内存图 3.二个引用指向同一个对象 4.this的内存布局图 创建了一个Student类的对象s1,因为有new,所有在堆区开辟了一些内存空间,比如把这些内存空间的地址值叫001 形参name是竹小玲, 也就是对象s调用method方法的地址值, this表示方法调用者的地…

Shell 学习笔记 - 导读 + 变量定义

初识 Shell 本章学习目标 了解什么是 Shell了解 Shell 的版本及用途掌握 Shell 变量的用法 导读&#xff08; 了解 \color{cyan}{了解} 了解&#xff09; 现在的人们使用的操作系统&#xff08;Windows、Android、iOS 等&#xff09;都带有图形化界面&#xff0c;简单直观&…

网络安全 - DDoS 攻击原理 + 实验

DDoS 攻击 什么是 DDoS 进攻 D D o S \color{cyan}{DDoS} DDoS&#xff08;Distributed Denial of Service&#xff0c;分布式拒绝服务&#xff09;攻击是一种通过多个计算机系统同时向目标系统发送大量请求&#xff0c;消耗其资源&#xff0c;使其无法正常服务的攻击方式。DD…