Semaphore通过permits的值来限制线程访问临界资源的总数,属于有限制次数的共享锁,不支持重入。

前提条件

在理解Semaphore时需要具备一些基本的知识:

理解AQS的实现原理

之前有写过一篇《深入浅出AQS源码解析》关于AQS的文章,对AQS原理不了解的同学可以先看一下

Semaphore源码解析

Semaphore中有3个内部类,分别是SyncNonfairSyncFairSyncSyncNonfairSyncFairSync的抽象类,我们会从解读Semaphore实现的功能开始入手逐渐去解析SyncNonfairSyncFairSync的源码

public class Semaphore implements java.io.Serializable {

    private final Sync sync;

    /**
* 初始化permits个资源
*/
public Semaphore(int permits) {
sync = new NonfairSync(permits);
} /**
* 初始化permits个资源,根据fair来决定是使用公平锁还是非公平锁的方式
*/
public Semaphore(int permits, boolean fair) {
sync = fair ? new FairSync(permits) : new NonfairSync(permits);
} /**
* 中断方式获取一个资源
*/
public void acquire() throws InterruptedException {
sync.acquireSharedInterruptibly(1);
} /**
* 非中断方式获取一个资源
*/
public void acquireUninterruptibly() {
sync.acquireShared(1);
} /**
* 尝试获取一个资源
*/
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 void release() {
sync.releaseShared(1);
} /**
* 中断方式获取permits个资源
*/
public void acquire(int permits) throws InterruptedException {
if (permits < 0) throw new IllegalArgumentException();
sync.acquireSharedInterruptibly(permits);
} /**
* 非中断方式获取permits个资源
*/
public void acquireUninterruptibly(int permits) {
if (permits < 0) throw new IllegalArgumentException();
sync.acquireShared(permits);
} /**
* 尝试获取permits个资源
*/
public boolean tryAcquire(int permits) {
if (permits < 0) throw new IllegalArgumentException();
return sync.nonfairTryAcquireShared(permits) >= 0;
} /**
* 尝试超时获取permits个资源
*/
public boolean tryAcquire(int permits, long timeout, TimeUnit unit)
throws InterruptedException {
if (permits < 0) throw new IllegalArgumentException();
return sync.tryAcquireSharedNanos(permits, unit.toNanos(timeout));
} /**
* 释放permits个资源
*/
public void release(int permits) {
if (permits < 0) throw new IllegalArgumentException();
sync.releaseShared(permits);
} /**
* 获取当前可用资源数量
*/
public int availablePermits() {
return sync.getPermits();
} /**
* 用掉所有的资源,并返回用掉的资源数量
*/
public int drainPermits() {
return sync.drainPermits();
} /**
* 缩减reduction个资源
*/
protected void reducePermits(int reduction) {
if (reduction < 0) throw new IllegalArgumentException();
sync.reducePermits(reduction);
}
}

虽然Semaphore中的方法比较多,但是都比较简单,都是转调用Sync中的方法,通过解析Sync中的源码来帮助大家理解这些方法是如何实现的

Sync类源码解析

abstract static class Sync extends AbstractQueuedSynchronizer {
// 获取所有可用的资源数量
final int getPermits() {
return getState();
}
// 非公平的方式尝试获取acquires个可用的资源
final int nonfairTryAcquireShared(int acquires) {
// 无限循环,尝试获取acquires个资源
// 如果资源数量不够,返回剩余资源数量
// 如果资源数量足够且获取成功,返回剩余的资源数量
for (;;) {
int available = getState();
int remaining = available - acquires;
if (remaining < 0 ||
compareAndSetState(available, remaining))
return remaining;
}
} // 尝试获取releases个资源
protected final boolean tryReleaseShared(int releases) {
for (;;) {
int current = getState();
int next = current + releases;
// 当releases不允许为负数
if (next < current) // overflow
throw new Error("Maximum permit count exceeded");
// CAS操作尝试修改state的值
if (compareAndSetState(current, next))
return true;
}
} // 缩减releases个资源
final void reducePermits(int reductions) {
for (;;) {
int current = getState();
int next = current - reductions;
// 当releases不允许为负数,也就时不能通过该方法增加资源
if (next > current) // underflow
throw new Error("Permit count underflow");
// CAS操作尝试修改state的值
if (compareAndSetState(current, next))
return;
}
} // 清空所有的资源数量
final int drainPermits() {
for (;;) {
int current = getState();
// CAS操作尝试将资源数量设置为0
if (current == 0 || compareAndSetState(current, 0))
return current;
}
}
}

FairSync类源码解析

FairSync中的源码很简单,直接上代码

static final class FairSync extends Sync {

    FairSync(int permits) {
super(permits);
} protected int tryAcquireShared(int acquires) {
/**
* 具体思路如下:
* 1、如果AQS的同步队列中有等待的线程,直接获取失败,会加入到AQS的同步队列中
* 2、如果AQS的同步队列为空,尝试修改state的值来获取acquires个资源
* 3、一直重复步骤1和2,直到有结果返回才退出无限循环
*/
for (;;) {
if (hasQueuedPredecessors())
return -1;
int available = getState();
int remaining = available - acquires;
if (remaining < 0 ||
compareAndSetState(available, remaining))
return remaining;
}
}
}

NonfairSync类源码解析

NonfairSync中的源码就更简单,解析如下:

static final class NonfairSync extends Sync {

    NonfairSync(int permits) {
super(permits);
} // 抢占式的获取acquires个资源
protected int tryAcquireShared(int acquires) {
return nonfairTryAcquireShared(acquires);
}
}

总结

  • permits初始化为1时,Semaphore变成了一个互斥的排他锁
  • permits初始化为无穷大时,Semaphore变成了无锁模式
  • state的值为0的时候,无法获取资源,获取资源的线程会进入AQS的同步队列等待有资源释放时被唤醒
  • Semaphore初始化成非公平锁时,可能会出现有的线程饿死的情况,一般对于控制资源的使用而言,建议初始化为公平锁
  • 可以调用reducePermits动态的缩减资源的数量,但是不能增加资源的数量

深入浅出Semaphore源码解析的更多相关文章

  1. 深入浅出ReentrantLock源码解析

    ReentrantLock不但是可重入锁,而且还是公平或非公平锁,在工作中会经常使用到,将自己对这两种锁的理解记录下来,希望对大家有帮助. 前提条件 在理解ReentrantLock时需要具备一些基本 ...

  2. 深入浅出ReentrantReadWriteLock源码解析

    读写锁实现逻辑相对比较复杂,但是却是一个经常使用到的功能,希望将我对ReentrantReadWriteLock的源码的理解记录下来,可以对大家有帮助 前提条件 在理解ReentrantReadWri ...

  3. 死磕 java同步系列之Semaphore源码解析

    问题 (1)Semaphore是什么? (2)Semaphore具有哪些特性? (3)Semaphore通常使用在什么场景中? (4)Semaphore的许可次数是否可以动态增减? (5)Semaph ...

  4. Java并发之Semaphore源码解析(一)

    Semaphore 前情提要:在学习本章前,需要先了解笔者先前讲解过的ReentrantLock源码解析,ReentrantLock源码解析里介绍的方法有很多是本章的铺垫.下面,我们进入本章正题Sem ...

  5. Java并发之Semaphore源码解析(二)

    在上一章,我们学习了信号量(Semaphore)是如何请求许可证的,下面我们来看看要如何归还许可证. 可以看到当我们要归还许可证时,不论是调用release()或是release(int permit ...

  6. Java并发包源码学习系列:同步组件Semaphore源码解析

    目录 Semaphore概述及案例学习 类图结构及重要字段 void acquire() 非公平 公平策略 void acquire(int permits) void acquireUninterr ...

  7. 深入浅出AQS源码解析

    最近一直在研究AQS的源码,希望可以更深刻的理解AQS的实现原理.虽然网上有很多关于AQS的源码分析,但是看完以后感觉还是一知半解.于是,我将自己的整个理解过程记录下来了,希望对大家有所帮助. 基本原 ...

  8. 死磕 java同步系列之CyclicBarrier源码解析——有图有真相

    问题 (1)CyclicBarrier是什么? (2)CyclicBarrier具有什么特性? (3)CyclicBarrier与CountDownLatch的对比? 简介 CyclicBarrier ...

  9. 死磕 java同步系列之Phaser源码解析

    问题 (1)Phaser是什么? (2)Phaser具有哪些特性? (3)Phaser相对于CyclicBarrier和CountDownLatch的优势? 简介 Phaser,翻译为阶段,它适用于这 ...

随机推荐

  1. matplotlib 强化学习

    matplotlib 强化学习 import matplotlib.pyplot as plt ...![](https://img2020.cnblogs.com/blog/1642028/2020 ...

  2. yum 安装包的时候提示“没有可用软件包”

    今天在使用 yum 命令进行包的下载时候,Linux 提示 没有可用的软件包~ 如下: [root@localhost share]# yum -y install wordpress 已加载插件:f ...

  3. Mac OS 生成 icon 和 ico 文件

    [本文版权归微信公众号"代码艺术"(ID:onblog)所有,若是转载请务必保留本段原创声明,违者必究.若是文章有不足之处,欢迎关注微信公众号私信与我进行交流!] 1. 生成 IC ...

  4. Android Studio出现:Cause: unable to find valid certification path to requested target问题解决

    Android Studio , Flutter , IDEA 工程报错 unable to find valid certification path to requested target 最新解 ...

  5. 八张图彻底了解JDK8 GC调优秘籍-附PDF下载

    目录 简介 分代垃圾回收器的内存结构 JDK8中可用的GC 打印GC信息 内存调整参数 Thread配置 通用GC参数 CMS GC G1参数 总结 简介 JVM的参数有很多很多,根据我的统计JDK8 ...

  6. 版本控制工具 GIT入门教程

    GIT 在团队中的中作流程 1.每个程序员在自己的分支上进行开发 2.主程序猿/Leader合并程序员程序 3.程序员之间也可以对一下提交冲突进行合并 下载和安装 GIT官方网址:http:// gi ...

  7. Spring学习笔记下载

    动力节点的spring视频教程相当的经典:下载地址 https://pan.baidu.com/s/1eTSOaae

  8. Idea激活JRebel

    选择license激活 UUID 生成地址: http://www.uuid.online 激活地址 http://jrebel-license.jiweichengzhu.com/ UUID

  9. Python实用笔记 (3)条件判断

    可以执行多条语句,靠的是缩进原则,看起来也更板扎(注意冒号) age = 3 if age >= 18: print('adult') elif age >= 6: print('teen ...

  10. dart快速入门教程 (4)

    4.流程控制 4.1.分支结构 1.if语句 void main() { int score = 80; if (score >= 90) { print('优秀'); } else if (s ...