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的更多相关文章

  1. Java源码分析系列之HttpServletRequest源码分析

    从源码当中 我们可以 得知,HttpServletRequest其实 实际上 并 不是一个类,它只是一个标准,一个 接口而已,它的 父类是ServletRequest. 认证方式 public int ...

  2. Java源码分析系列

    1) 深入Java集合学习系列:HashMap的实现原理 2) 深入Java集合学习系列:LinkedHashMap的实现原理 3) 深入Java集合学习系列:HashSet的实现原理 4) 深入Ja ...

  3. jQuery源码分析系列

    声明:本文为原创文章,如需转载,请注明来源并保留原文链接Aaron,谢谢! 版本截止到2013.8.24 jQuery官方发布最新的的2.0.3为准 附上每一章的源码注释分析 :https://git ...

  4. MyCat源码分析系列之——结果合并

    更多MyCat源码分析,请戳MyCat源码分析系列 结果合并 在SQL下发流程和前后端验证流程中介绍过,通过用户验证的后端连接绑定的NIOHandler是MySQLConnectionHandler实 ...

  5. MyCat源码分析系列之——BufferPool与缓存机制

    更多MyCat源码分析,请戳MyCat源码分析系列 BufferPool MyCat的缓冲区采用的是java.nio.ByteBuffer,由BufferPool类统一管理,相关的设置在SystemC ...

  6. jquery2源码分析系列

    学习jquery的源码对于提高前端的能力很有帮助,下面的系列是我在网上看到的对jquery2的源码的分析.等有时间了好好研究下.我们知道jquery2开始就不支持IE6-8了,从jquery2的源码中 ...

  7. [Tomcat 源码分析系列] (二) : Tomcat 启动脚本-catalina.bat

    概述 Tomcat 的三个最重要的启动脚本: startup.bat catalina.bat setclasspath.bat 上一篇咱们分析了 startup.bat 脚本 这一篇咱们来分析 ca ...

  8. [转]jQuery源码分析系列

    文章转自:jQuery源码分析系列-Aaron 版本截止到2013.8.24 jQuery官方发布最新的的2.0.3为准 附上每一章的源码注释分析 :https://github.com/JsAaro ...

  9. MyBatis 源码分析系列文章导读

    1.本文速览 本篇文章是我为接下来的 MyBatis 源码分析系列文章写的一个导读文章.本篇文章从 MyBatis 是什么(what),为什么要使用(why),以及如何使用(how)等三个角度进行了说 ...

  10. spring源码分析系列 (8) FactoryBean工厂类机制

    更多文章点击--spring源码分析系列 1.FactoryBean设计目的以及使用 2.FactoryBean工厂类机制运行机制分析 1.FactoryBean设计目的以及使用 FactoryBea ...

随机推荐

  1. 一文速通 Python 并行计算:05 Python 多线程编程-线程的定时运行

    一文速通 Python 并行计算:05 Python 多线程编程-线程的定时运行 摘要: 本文主要讲述了 Python 如何实现定时任务,主要有四种方式:通过 threading.Timer 类.通过 ...

  2. Cloud Native CI/CD: tekton and argocd

    https://platform9.com/blog/argo-cd-vs-tekton-vs-jenkins-x-finding-the-right-gitops-tooling/ implemen ...

  3. 🎀Idea插件-arthas idea

    简介 Arthas Idea 是一个为 IntelliJ IDEA 开发的插件,它是为了方便使用 Arthas 这个 Java 诊断工具而设计的.Arthas 是阿里巴巴开源的一款强大的 Java 应 ...

  4. journalctl -u docker 查看日志

    转载注明出处: 1. 查看 Docker 服务的最新日志(实时滚动) sudo journalctl -u docker -f -f 参数表示 跟随(follow),会持续输出最新日志(类似 tail ...

  5. SpringBoot Task定时任务

    参数详解 @Target({ElementType.METHOD, ElementType.ANNOTATION_TYPE}) @Retention(RetentionPolicy.RUNTIME) ...

  6. 题解:CF1955E Long Inversions

    简单题. 考虑贪心地进行修改,每次选择字符串中最左侧第一个 000,并以该位置为左端点进行一次修改,可以发现若 lenlenlen 合法则这样一定构造出全 111 串. 然而直接暴力实现是 O(n2) ...

  7. 【经验】Ubuntu18.04切换Python版本及环境,及VScode/pdb调试方法(全)

    文章目录 安装Python并切换 创建Python虚拟环境 方案一:virtualenv+virtualenvwrapper 方案二:venv 方案三:Anaconda 方案四:pipenv Pyth ...

  8. Jmeter+Ant+Jenkins接口自动化测试(三)_Ant配置及Jenkins持续集成

    前言: 本来想多分几部分,但是都是抽时间总结的,也就不润色了,直接三板斧,结束. 特别提示: 知识是用来分享的,但是也要尊重作者的权益,转载请注明出处,未经本人允许不可用于商业目的. Ant构建文件配 ...

  9. vue3 基础-表单元素双向绑定

    通常是在 form 表单相关的场景中会用到双向绑定相关, 核心是 v-model 的应用. input 输入框 <!DOCTYPE html> <html lang="en ...

  10. 10个让你成为CSS画家的技巧,不容错过

    @charset "UTF-8"; .markdown-body { line-height: 1.75; font-weight: 400; font-size: 15px; o ...