Java源码分析系列笔记-18.Semaphore
1. 是什么
限流工具类,同一时间只允许n个线程访问某资源
2. 原理分析
2.1. uml
@startuml
skinparam classAttributeIconSize 0
class Semaphore{
}
class AbstractQueuedSynchronizer{
}
class Sync{
}
class FairSync{
}
class NonfairSync{
}
Sync <|-- FairSync
Sync <|-- NonfairSync
AbstractQueuedSynchronizer <|-- Sync
Semaphore --> Sync
@enduml
可以看出Semaphore也有公平的和非公平之分
3. 公平信号量
3.1. 是什么
限流,使用的公平策略
3.2. 使用
public class SemaphoreTest
{
private final static int THREAD_COUNT = 100;
private final static CountDownLatch countDownLatch = new CountDownLatch(THREAD_COUNT);
public static void main(String[] args) throws InterruptedException
{
Semaphore semaphore = new Semaphore(10, true);//true代表公平
for (int i = 0; i < THREAD_COUNT; i++)
{
new Thread(()->{
try
{
semaphore.acquire();
System.out.println(Thread.currentThread().getName() + "正在访问资源。。。");
TimeUnit.SECONDS.sleep(3);
}
catch (Exception e)
{
e.printStackTrace();
}
finally
{
semaphore.release();
countDownLatch.countDown();
}
}).start();
}
countDownLatch.await();
}
}
3.3. 原理分析
3.3.1. 构造方法
public Semaphore(int permits, boolean fair) {
//公平用的是FairSync
sync = fair ? new FairSync(permits) : new NonfairSync(permits);
}
3.3.1.1. 公平Sync
- FairSync
static final class FairSync extends Sync {
private static final long serialVersionUID = 2014338818796000944L;
FairSync(int permits) {
//Semaphore.Sync#Sync
super(permits);
}
protected int tryAcquireShared(int acquires) {
for (;;) {
if (hasQueuedPredecessors())
return -1;
int available = getState();
int remaining = available - acquires;
if (remaining < 0 ||
compareAndSetState(available, remaining))
return remaining;
}
}
}
- Sync
abstract static class Sync extends AbstractQueuedSynchronizer {
private static final long serialVersionUID = 1192457210091910933L;
Sync(int permits) {
//最终就是设置了permits个信号量
setState(permits);
}
}
3.3.2. acquire
public void acquire() throws InterruptedException {
//AQS的acquireSharedInterruptibly
sync.acquireSharedInterruptibly(1);
}
3.3.2.1. 调用AQS加共享锁
- AQS acquireSharedInterruptibly
public final void acquireSharedInterruptibly(int arg)
throws InterruptedException {
if (Thread.interrupted())
throw new InterruptedException();
//Semaphore.FairSync重写了tryAcquireShared
//如果信号量不够那么返回负数,这里执行doAcquireSharedInterruptibly入AQS队,并且阻塞等待唤醒
//如果信号量够的话返回>=0的数,那么调用此acquire方法的代码就可以继续执行业务代码
if (tryAcquireShared(arg) < 0)
doAcquireSharedInterruptibly(arg);
}
3.3.2.1.1. 尝试加锁【公平:队列前面有人排队那么直接返回失败】
- Semaphore.FairSync#tryAcquireShared
protected int tryAcquireShared(int acquires) {
for (;;) {
//如果队列中我的前面有人排队,那么返回-1
if (hasQueuedPredecessors())
return -1;
//当前的信号量
int available = getState();
//当前的信号量够不够我获取?
int remaining = available - acquires;
//<0表示不够的话返回这个数
if (remaining < 0 ||
//>=0说明够了,那么CAS修改剩余信号量
compareAndSetState(available, remaining))
return remaining;
}
}
3.3.3. release
- Semaphore#release()
public void release() {
//AQS的releaseShared
sync.releaseShared(1);
}
3.3.3.1. 调用AQS释放共享锁
- AQS#releaseShared
public final boolean releaseShared(int arg) {
//Semaphore.Sync重写了tryReleaseShared
if (tryReleaseShared(arg)) {
doReleaseShared();
return true;
}
return false;
}
3.3.3.1.1. 尝试释放共享锁
- Semaphore.Sync#tryReleaseShared
protected final boolean tryReleaseShared(int releases) {
for (;;) {
//获取当前信号量
int current = getState();
//加回去
int next = current + releases;
//溢出则抛异常
if (next < current) // overflow
throw new Error("Maximum permit count exceeded");
//CAS修改信号量
if (compareAndSetState(current, next))
return true;
}
}
4. 非公平信号量
4.1. 是什么
限流,使用的非公平策略
4.2. 使用
public class SemaphoreTest
{
private final static int THREAD_COUNT = 100;
private final static CountDownLatch countDownLatch = new CountDownLatch(THREAD_COUNT);
public static void main(String[] args) throws InterruptedException
{
Semaphore semaphore = new Semaphore(10);
for (int i = 0; i < THREAD_COUNT; i++)
{
new Thread(()->{
try
{
semaphore.acquire();
System.out.println(Thread.currentThread().getName() + "正在访问资源。。。");
TimeUnit.SECONDS.sleep(3);
}
catch (Exception e)
{
e.printStackTrace();
}
finally
{
semaphore.release();
countDownLatch.countDown();
}
}).start();
}
countDownLatch.await();
}
}
4.3. 原理分析
4.3.1. 构造方法
public Semaphore(int permits) {
//创建的是非公平的Sync
sync = new NonfairSync(permits);
}
4.3.1.1. 非公平Sync
- NonfairSync
static final class NonfairSync extends Sync {
private static final long serialVersionUID = -2694183684443567898L;
NonfairSync(int permits) {
//调用Sync
super(permits);
}
protected int tryAcquireShared(int acquires) {
return nonfairTryAcquireShared(acquires);
}
}
- Sync
abstract static class Sync extends AbstractQueuedSynchronizer {
private static final long serialVersionUID = 1192457210091910933L;
Sync(int permits) {
//最终就是设置了permits个信号量
setState(permits);
}
}
4.3.2. acquire
public void acquire() throws InterruptedException {
//AQS的acquireSharedInterruptibly
sync.acquireSharedInterruptibly(1);
}
4.3.2.1. 调用AQS加共享锁
- AQS acquireSharedInterruptibly
public final void acquireSharedInterruptibly(int arg)
throws InterruptedException {
if (Thread.interrupted())
throw new InterruptedException();
//Semaphore.NonfairSync重写了tryAcquireShared
//如果信号量不够那么返回负数,这里执行doAcquireSharedInterruptibly入AQS队,并且阻塞等待唤醒
//如果信号量够的话返回>=0的数,那么调用此acquire方法的代码就可以继续执行业务代码
if (tryAcquireShared(arg) < 0)
doAcquireSharedInterruptibly(arg);
}
4.3.2.1.1. 尝试加共享锁【非公平:不管队列前面是否有人我直接尝试加锁】
- Semaphore.NonfairSync#tryAcquireShared
protected int tryAcquireShared(int acquires) {
return nonfairTryAcquireShared(acquires);
}
- Semaphore.Sync#nonfairTryAcquireShared
final int nonfairTryAcquireShared(int acquires) {
for (;;) {
//当前的信号量
int available = getState();
//当前的信号量够不够我获取?
int remaining = available - acquires;
//<0表示不够的话返回这个数
if (remaining < 0 ||
//>=0说明够了,那么CAS修改剩余信号量
compareAndSetState(available, remaining))
return remaining;
}
}
4.3.3. release
- Semaphore#release()
public void release() {
//AQS的releaseShared
sync.releaseShared(1);
}
4.3.3.1. 调用AQS释放共享锁
- AQS#releaseShared
public final boolean releaseShared(int arg) {
//Semaphore.Sync重写了tryReleaseShared
if (tryReleaseShared(arg)) {
doReleaseShared();
return true;
}
return false;
}
4.3.3.1.1. 尝试释放锁
- Semaphore.Sync#tryReleaseShared
protected final boolean tryReleaseShared(int releases) {
for (;;) {
//获取当前信号量
int current = getState();
//加回去
int next = current + releases;
//溢出则抛异常
if (next < current) // overflow
throw new Error("Maximum permit count exceeded");
//CAS修改信号量
if (compareAndSetState(current, next))
return true;
}
}
Java源码分析系列笔记-18.Semaphore的更多相关文章
- Java源码分析系列之HttpServletRequest源码分析
从源码当中 我们可以 得知,HttpServletRequest其实 实际上 并 不是一个类,它只是一个标准,一个 接口而已,它的 父类是ServletRequest. 认证方式 public int ...
- Java源码分析系列
1) 深入Java集合学习系列:HashMap的实现原理 2) 深入Java集合学习系列:LinkedHashMap的实现原理 3) 深入Java集合学习系列:HashSet的实现原理 4) 深入Ja ...
- jQuery源码分析系列
声明:本文为原创文章,如需转载,请注明来源并保留原文链接Aaron,谢谢! 版本截止到2013.8.24 jQuery官方发布最新的的2.0.3为准 附上每一章的源码注释分析 :https://git ...
- MyCat源码分析系列之——结果合并
更多MyCat源码分析,请戳MyCat源码分析系列 结果合并 在SQL下发流程和前后端验证流程中介绍过,通过用户验证的后端连接绑定的NIOHandler是MySQLConnectionHandler实 ...
- MyCat源码分析系列之——BufferPool与缓存机制
更多MyCat源码分析,请戳MyCat源码分析系列 BufferPool MyCat的缓冲区采用的是java.nio.ByteBuffer,由BufferPool类统一管理,相关的设置在SystemC ...
- jquery2源码分析系列
学习jquery的源码对于提高前端的能力很有帮助,下面的系列是我在网上看到的对jquery2的源码的分析.等有时间了好好研究下.我们知道jquery2开始就不支持IE6-8了,从jquery2的源码中 ...
- [Tomcat 源码分析系列] (二) : Tomcat 启动脚本-catalina.bat
概述 Tomcat 的三个最重要的启动脚本: startup.bat catalina.bat setclasspath.bat 上一篇咱们分析了 startup.bat 脚本 这一篇咱们来分析 ca ...
- [转]jQuery源码分析系列
文章转自:jQuery源码分析系列-Aaron 版本截止到2013.8.24 jQuery官方发布最新的的2.0.3为准 附上每一章的源码注释分析 :https://github.com/JsAaro ...
- MyBatis 源码分析系列文章导读
1.本文速览 本篇文章是我为接下来的 MyBatis 源码分析系列文章写的一个导读文章.本篇文章从 MyBatis 是什么(what),为什么要使用(why),以及如何使用(how)等三个角度进行了说 ...
- spring源码分析系列 (8) FactoryBean工厂类机制
更多文章点击--spring源码分析系列 1.FactoryBean设计目的以及使用 2.FactoryBean工厂类机制运行机制分析 1.FactoryBean设计目的以及使用 FactoryBea ...
随机推荐
- pandas数据统一绘图风格配置
在使用pandas的时候,经常会用到Dataframe或者Series的plot方法,该方法底层实际上调的还是matplotlib.pyplot的plot方法.因此,通过对pyplot模块的绘图全局参 ...
- HarmonyOS NEXT 基于原生能力获取视频缩略图
大家好,我是 V 哥. 不得不佩服 HarmonyOS NEXT 原生能力的强大,如果你想在 鸿蒙 APP 开发中获取视频缩略图,不用依赖第三方库,就可以高效和稳定的实现,AVMetadataHelp ...
- [源码系列:手写spring] IOC第十三节:Bean作用域,增加prototype的支持
为了帮助大家更深入的理解bean的作用域,特意将BeanDefinition的双例支持留到本章节中,创建Bean,相关Reader读取等逻辑都有所改动. 内容介绍 在Spring中,Bean的作用域( ...
- 【SpringMVC】视图和视图解析器
视图和视图解析器 Spring MVC如何解析视图 视图和视图解析器 请求处理方法执行完成后,最终返回一个 ModelAndView对象.对于那些返回 String,View 或 ModeMap 等类 ...
- 【教程】C语言入门
C语言入门 首先导入头文件 #include<stdio.h> 接下来编写主函数 #include<stdio.h> int main() { retuen 0; } 最后,在 ...
- ASP.NET Core 静态资源的打包与压缩
以 Visual Studio Community 2017 15.5.1 为例 配置文件 bundleconfig.json 新建一个AspNetCore MVC项目,项目中会有一个bundleco ...
- android中大咖:TlistView
d的android中的Tlistview相当于cxGrid,其开发使用中的主咖地位至高无上. 可是如何高效使用快速实现的需求呢?需要的话补官方的教程 上图: GOODLUCK!
- SearXNG私有化部署与Dify集成
一.概述 SearXNG 是一个免费的互联网元搜索引擎,它聚合了来自各种搜索服务和数据库的结果,但摆脱了隐私追踪 -- 用户行为既不会被引擎跟踪也不会被分析. 功能特性 自托管,可以私有化部署 没有用 ...
- 依赖注入的方式( 构造函数注入 、 set 方法注入 、注解注入)
一.构造函数注入 二.set方式注入 三.集合注入
- 从 Excel 到你的表格应用:数据验证功能的嵌入实践指南
前言: 随着信息化的不断发展,传统表格软件已无法满足用户对便携性.数据自动化管理等日益复杂的要求,将电子表格与其他系统结合.开发自己的表格应用已成为愈发火热的趋势. 然而,当企业需要将 Excel 的 ...