Java并发 JUC工具类:Semaphore详解

news/2024/5/15 10:25:11/文章来源:https://blog.csdn.net/weixin_45773632/article/details/126682056

文章目录

  • Semaphore源码分析
    • 类的继承关系
    • 类的内部类
    • 类的内部类 - Sync类
    • 类的内部类 - NonfairSync类
    • 类的内部类 - FairSync类
    • 类的属性
    • 类的构造函数
    • 核心函数分析 - acquire函数
    • 核心函数分析 - release函数
  • Semaphore 示例
  • 更深入理解
    • 单独使用Semaphore是不会使用到AQS的条件队列的
    • 场景问题

Semaphore源码分析

类的继承关系

public class Semaphore implements java.io.Serializable {}

说明: Semaphore实现了Serializable接口,即可以进行序列化。

类的内部类

Semaphore总共有三个内部类,并且三个内部类是紧密相关的,下面先看三个类的关系。
在这里插入图片描述
说明: Semaphore与ReentrantLock的内部类的结构相同,类内部总共存在Sync、NonfairSync、FairSync三个类,NonfairSync与FairSync类继承自Sync类,Sync类继承自AbstractQueuedSynchronizer抽象类。下面逐个进行分析。

类的内部类 - Sync类

Sync类的源码如下:

著作权归https://pdai.tech所有。
链接:https://pdai.tech/md/java/thread/java-thread-x-juc-tool-semaphore.html// 内部类,继承自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)) // 许可小于0或者比较并且设置状态成功return remaining;}}// 共享模式下进行释放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;}}// 根据指定的缩减量减小可用许可的数目final void reducePermits(int reductions) {for (;;) { // 无限循环// 获取许可int current = getState();// 可用的许可int next = current - reductions;if (next > current) // underflowthrow new Error("Permit count underflow");if (compareAndSetState(current, next)) // 比较并进行设置成功return;}}// 获取并返回立即可用的所有许可final int drainPermits() {for (;;) { // 无限循环// 获取许可int current = getState();if (current == 0 || compareAndSetState(current, 0)) // 许可为0或者比较并设置成功return current;}}
}

说明: Sync类的属性相对简单,只有一个版本号,Sync类存在如下方法和作用如下。
在这里插入图片描述

类的内部类 - NonfairSync类

NonfairSync类继承了Sync类,表示采用非公平策略获取资源,其只有一个tryAcquireShared方法,重写了AQS的该方法,其源码如下:

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);}
}

说明: 从tryAcquireShared方法的源码可知,其会调用父类Sync的nonfairTryAcquireShared方法,表示按照非公平策略进行资源的获取。

类的内部类 - FairSync类

FairSync类继承了Sync类,表示采用公平策略获取资源,其只有一个tryAcquireShared方法,重写了AQS的该方法,其源码如下。

protected int tryAcquireShared(int acquires) {for (;;) { // 无限循环if (hasQueuedPredecessors()) // 同步队列中存在其他节点return -1;// 获取许可int available = getState();// 剩余的许可int remaining = available - acquires;if (remaining < 0 ||compareAndSetState(available, remaining)) // 剩余的许可小于0或者比较设置成功return remaining;}
}

类的属性

public class Semaphore implements java.io.Serializable {// 版本号private static final long serialVersionUID = -3222578661600680210L;// 属性private final Sync sync;
}

说明: Semaphore自身只有两个属性,最重要的是sync属性,基于Semaphore对象的操作绝大多数都转移到了对sync的操作。

类的构造函数

  • Semaphore(int)型构造函数
public Semaphore(int permits) {sync = new NonfairSync(permits);
}

说明: 该构造函数会创建具有给定的许可数和非公平的公平设置的Semaphore。

  • Semaphore(int, boolean)型构造函数
public Semaphore(int permits, boolean fair) {sync = fair ? new FairSync(permits) : new NonfairSync(permits);
}

说明: 该构造函数会创建具有给定的许可数和给定的公平设置的Semaphore。

核心函数分析 - acquire函数

此方法从信号量获取一个(多个)许可,在提供一个许可前一直将线程阻塞,或者线程被中断,其源码如下

public void acquire() throws InterruptedException {sync.acquireSharedInterruptibly(1);
}

说明: 该方法中将会调用Sync对象的acquireSharedInterruptibly(从AQS继承而来的方法)方法,而acquireSharedInterruptibly方法在上一篇CountDownLatch中已经进行了分析。

最终可以获取大致的方法调用序列(假设使用非公平策略)。如下图所示。
在这里插入图片描述
说明: 上图只是给出了大体会调用到的方法,和具体的示例可能会有些差别,之后会根据具体的示例进行分析。

核心函数分析 - release函数

此方法释放一个(多个)许可,将其返回给信号量,源码如下。

public void release() {sync.releaseShared(1);
}

说明: 该方法中将会调用Sync对象的releaseShared(从AQS继承而来的方法)方法,而releaseShared方法在上一篇CountDownLatch中已经进行了分析。

最终可以获取大致的方法调用序列(假设使用非公平策略)。如下图所示:
在这里插入图片描述
说明: 上图只是给出了大体会调用到的方法,和具体的示例可能会有些差别,之后会根据具体的示例进行分析。

Semaphore 示例

原文链接:https://pdai.tech/md/java/thread/java-thread-x-juc-tool-semaphore.html#semaphore%E7%A4%BA%E4%BE%8B

import java.util.concurrent.Semaphore;class MyThread extends Thread {private Semaphore semaphore;public MyThread(String name, Semaphore semaphore) {super(name);this.semaphore = semaphore;}public void run() {int count = 3;System.out.println(Thread.currentThread().getName() + " trying to acquire");try {semaphore.acquire(count);System.out.println(Thread.currentThread().getName() + " acquire successfully");Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();} finally {semaphore.release(count);System.out.println(Thread.currentThread().getName() + " release successfully");}}
}public class SemaphoreDemo {public final static int SEM_SIZE = 10;public static void main(String[] args) {Semaphore semaphore = new Semaphore(SEM_SIZE);MyThread t1 = new MyThread("t1", semaphore);MyThread t2 = new MyThread("t2", semaphore);t1.start();t2.start();//main线程int permits = 5;System.out.println(Thread.currentThread().getName() + " trying to acquire");try {semaphore.acquire(permits);System.out.println(Thread.currentThread().getName() + " acquire successfully");Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();} finally {semaphore.release();System.out.println(Thread.currentThread().getName() + " release successfully");}}
}

运行结果(某一次):

main trying to acquire
main acquire successfully
t1 trying to acquire
t1 acquire successfully
t2 trying to acquire
t1 release successfully
main release successfully
t2 acquire successfully
t2 release successfully

说明: 首先,生成一个信号量,信号量有10个许可,然后,main,t1,t2三个线程获取许可运行,根据结果,可能存在如下的一种时序。

在这里插入图片描述

更深入理解

单独使用Semaphore是不会使用到AQS的条件队列的

不同于CyclicBarrier和ReentrantLock,单独使用Semaphore是不会使用到AQS的条件队列的,其实,只有进行await操作才会进入条件队列,其他的都是在同步队列中,只是当前线程会被park。

场景问题

  • semaphore初始化有10个令牌,11个线程同时各调用1次acquire方法,会发生什么?
    答案:拿不到令牌的线程阻塞,不会继续往下运行。
  • semaphore初始化有10个令牌,一个线程重复调用11次acquire方法,会发生什么?
    答案:线程阻塞,不会继续往下运行。可能你会考虑类似于锁的重入的问题,很好,但是,令牌没有重入的概念。你只要调用一次acquire方法,就需要有一个令牌才能继续运行。
  • semaphore初始化有1个令牌,1个线程调用一次acquire方法,然后调用两次release方法,之后另外一个线程调用acquire(2)方法,此线程能够获取到足够的令牌并继续运行吗?
    答案:能,原因是release方法会添加令牌,并不会以初始化的大小为准。

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

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

相关文章

USB4 V2.0即将发布,速度高达80Gbps

关注星标公众号&#xff0c;不错过精彩内容作者 | strongerHuang微信公众号 | strongerHuang2019年3月初&#xff0c;USB-IF组织官方宣布了下一代 USB4.2019年9月3日&#xff0c;USB-IF组织正式发布 USB4(现在称USB4 V1.0)规范。最近&#xff0c;也就是2022年9月1日&#xff0c…

推荐系统中的特征工程

摘要&#xff1a;深度学习时期&#xff0c;与CV、语音、NLP领域不同&#xff0c;搜推广场景下特征工程仍然对业务效果具有很大的影响&#xff0c;并且占据了算法工程师的很多精力。数据决定了效果的上限&#xff0c;算法只能决定逼近上限的程度&#xff0c;而特征工程则是数据与…

uniapp 之 获取底部安全距离,状态栏高度等

特定样式注意点固定底部按钮自定义顶部导航栏其他工作中我们常常需要设置一些特定样式&#xff1a; 固定底部按钮自定义顶部导航栏…… 固定底部按钮 这里需要注意的是&#xff0c;真机运行时底部时IOS是存在安全距离的&#xff0c;这个时候就需要我们处理一下 .u-fixed-b …

Echarts y轴相关配置

目录1 简介2 y轴配置2.1 y轴主要属性2.2 y轴刻度设置3.总结1 简介 本篇介绍我们在使用Echarts画图时常用的一些y轴坐标设置&#xff0c;如y轴位置&#xff0c;y轴偏移量、y轴刻度、y轴最大最小值等&#xff1b; 2 y轴配置 2.1 y轴主要属性 只有一个纵坐标的情况下&#xff0…

TLM通信总结1

事务级建模 (TLM) 用于模块之间的通信。 TLM 是实现基于事务的方法的概念,这些方法可用于模块之间的通信。 UVM TLM UVM 为 TLM 库提供事务级接口,ports,exports,imp ports,and analysis ports。所有这些 TLM 元素都需要发送事务、接收事务以及从一个组件传输到另一个组件…

STM32物联网项目-程序框架思想

程序框架思想 一、程序框架的构想 1、STM32cubeMX生成的代码与添加的应用代码分离; 2、利用STM32cubeMX重新生成代码&#xff0c;不影响应用代码; 3、应用代码的添加&#xff0c;移除与修改&#xff0c;不影响cube生成的代码; 4、代码架构方便阅读&#xff0c;编辑&#x…

领域最全!多传感器融合方法综述!(Camera/Lidar/Radar等多源异构数据)

点击进入→自动驾驶之心技术交流群 后台回复【ECCV2022】获取ECCV2022所有自动驾驶方向论文! 自动驾驶中的多传感器融合 原文:Multi-Sensor Fusion in Automated Driving: A Survey 自动驾驶正成为影响未来行业的关键技术,传感器是自动驾驶系统中感知外部世界的关键,其协作…

Java 开发中的 Lombok 是什么?

一. 血案 今天有个学生告诉我&#xff0c;他在项目中使用Mybatis框架查询时报错&#xff0c;提示无法创建对象。但自己仔细地检查了代码都没有发现错误&#xff0c;于是他就半夜拼命地给我发消息求救。 我起床拿手机&#xff0c;还差点摔倒闪了我的老腰&#xff0c;我老婆看我…

猿创征文|工作中遇到技术盲区后的自我成长

猿创征文&#xff5c;工作中遇到技术盲区后的自我成长 1、立场 我是一名python后端开发程序员&#xff0c;在一家创业公司中兢兢业业工作快两年了&#xff0c;从软件架构、开发、测试、部署、运维一手经办&#xff0c;到开发文档、API接口、开发周期、设备交付、安装完成全程…

面向对象编程原则(03)——单一职责原则

版权声明 本文原创作者&#xff1a;谷哥的小弟作者博客地址&#xff1a;http://blog.csdn.net/lfdfhl 参考资料 《大话设计模式》 作者&#xff1a;程杰《Java设计模式》 作者&#xff1a;刘伟《图解设计模式》 作者&#xff1a;结城浩《重学Java设计模式》 作者&#xff1a;…

[论文阅读] HairGAN: Spatial-Aware Palette GAN for Hair Color Transfer

[论文地址] [代码] [ICME 22] Abstract 头发颜色转移的目的是将头发颜色从参考图像转移到原始图像&#xff0c;同时保持原始图像的头发结构。然而&#xff0c;由于复杂的头发结构以及原始图像和参考图像之间头发区域的错位&#xff0c;现有的方法不能很好地完成这一任务。为了…

CTFshow_MISC入门_图片篇(基础操作信息附加)wp

文章目录前言Tipsmisc1misc2misc3misc4misc5misc6misc7misc8misc9misc10misc11后记前言 挺长时间没有打CTF了&#xff0c;感觉技术从之前就一直没有提升多少&#xff0c;摸了段时间的渗透&护网&#xff0c;感觉CTF的基础还是比较重要&#xff0c;温故而知新&#xff0c;就…

03.thymeleaf在业务系统中的应用

thymeleaf是Java方向开源的服务端模板引擎&#xff0c;支持多种格式的格式渲染。在存前端项目盛行的年代&#xff0c;webUI纯服务端渲染已经不再适合&#xff0c;但并不影响服务端模板的继续应用。 在企业集成业务系统中&#xff0c;由于低代码平台的业务标准化/组件化/所见及…

[ Linux长征路第三篇 ] 权限理解

目录 1.root用户和普通用户相互切换 2.文件类型和访问权限(事物属性) 2.1 文件类型 2.2 基本权限 2.3 文件权限值得表示方法 1&#xff09;字符表示法 2&#xff09;8进制数值表示法 2.4 文件访问权限的相关设置方法 1) chomd 2&#xff09;三位8进制数字 3) cho…

解决找回密码不在右边的问题

问题如下: 在10.5中,按照书中代码写法为float-right,则出现上述的问题,找回密码不能和书中的结果一样呈现在右边,而出现在了左边 解决问题:把float-right改为float-end即可。其实这也不是什么大问题,不过可能会有的同学可能找不到解决方法,所以在这里写一下。因为淋过…

2022java-web一条龙工具安装

@目录java安装java-jdk安装java环境变量配置java-eclipse工具安装Java-idea工具安装MySQL安装navicat安装tomcat安装maven安装配置本地仓库配置镜像配置jdk 备忘~安装常见的一些我需要的集成工具以及jdk java安装 java-jdk安装 1,去官网进行安装下载jdk2,找到下载jdk的文件位…

json/xml/schema

JSON JSON是JavaScript Object Notation的缩写&#xff0c;是一种轻量级的数据交换格式&#xff0c;是理想的接口数据交换语言。官网&#xff1a;https://www.json.org/json-en.html 工作json请求体&#xff1a; json字符串 hashmap对象 jackson库 json响应结果断言 语法…

堆优化dijkstra的两种写法

例题: https://www.acwing.com/problem/content/description/1131/ 1、仅用dis数组记录,出队时记录最小距离#include<bits/stdc++.h>#define fore(x,y,z) for(LL x=(y);x<=(z);x++) #define forn(x,y,z) for(LL x=(y);x<(z);x++) #define rofe(x,y,z) for(LL x=(…

瑞吉外卖git

文章目录&#x1f492; Git&#x1f68f; 1、Git 概述&#x1f680; Git 简介&#x1f684; 下载与安装&#x1f68f; 2、Git 代码托管服务&#x1f680; 常用的 Git 代码托管服务&#x1f684; 使用码云代码托管服务&#x1f6ac; 使用码云的操作流程如下&#xff1a;&#x1…

程序人生 | 编程的上帝视角应该怎么去找

前言 &#x1f4eb;作者简介&#xff1a;小明java问道之路&#xff0c;专注于Linux内核/汇编/HotSpot/C/Java/源码/架构/算法 就职于大型金融公司后端高级工程师&#xff0c;擅长交易领域的高安全/可用/并发/性能的架构设计&#x1f4eb; &#x1f3c6; CSDN专家博主/Java优质…