文档章节

Semaphore源码分析

harries
 harries
发布于 07/12 11:30
字数 1387
阅读 460
收藏 4
点赞 0
评论 0

源码解析

Semaphore有两种模式,公平模式和非公平模式。公平模式就是调用acquire的顺序就是获取许可证的顺序,遵循FIFO;而非公平模式是抢占式的,也就是有可能一个新的获取线程恰好在一个许可证释放时得到了这个许可证,而前面还有等待的线程。

构造方法

Semaphore有两个构造方法,如下:

       public Semaphore(int permits) {
        sync = new NonfairSync(permits);
    }

    public Semaphore(int permits, boolean fair) {
        sync = fair ? new FairSync(permits) : new NonfairSync(permits);
    }

从上面可以看到两个构造方法,都必须提供许可的数量,第二个构造方法可以指定是公平模式还是非公平模式,默认非公平模式。
Semaphore内部基于AQS的共享模式,所以实现都委托给了Sync类。
这里就看一下NonfairSync的构造方法:

 NonfairSync(int permits) {
            super(permits);
        }

可以看到直接调用了父类的构造方法,Sync的构造方法如下:

Sync(int permits) {
            setState(permits);
        }

可以看到调用了setState方法,也就是说AQS中的资源就是许可证的数量。

获取许可

先从获取一个许可看起,并且先看非公平模式下的实现。首先看acquire方法,acquire方法有几个重载,但主要是下面这个方法

public void acquire(int permits) throws InterruptedException {
        if (permits < 0) throw new IllegalArgumentException();
        sync.acquireSharedInterruptibly(permits);
    }

从上面可以看到,调用了Sync的acquireSharedInterruptibly方法,该方法在父类AQS中,如下:

public final void acquireSharedInterruptibly(int arg)
            throws InterruptedException {
        //如果线程被中断了,抛出异常
        if (Thread.interrupted())
            throw new InterruptedException();
        //获取许可失败,将线程加入到等待队列中
        if (tryAcquireShared(arg) < 0)
            doAcquireSharedInterruptibly(arg);
    }

AQS子类如果要使用共享模式的话,需要实现tryAcquireShared方法,下面看NonfairSync的该方法实现:

 protected int tryAcquireShared(int acquires) {
            return nonfairTryAcquireShared(acquires);
        }

该方法调用了父类中的nonfairTyAcquireShared方法,如下:

final int nonfairTryAcquireShared(int acquires) {
            for (;;) {
                //获取剩余许可数量
                int available = getState();
                //计算给完这次许可数量后的个数
                int remaining = available - acquires;
                //如果许可不够或者可以将许可数量重置的话,返回
                if (remaining < 0 ||
                    compareAndSetState(available, remaining))
                    return remaining;
            }
        }

从上面可以看到,只有在许可不够时返回值才会小于0,其余返回的都是剩余许可数量,这也就解释了,一旦许可不够,后面的线程将会阻塞。看完了非公平的获取,再看下公平的获取,代码如下:

 protected int tryAcquireShared(int acquires) {
            for (;;) {
                //如果前面有线程再等待,直接返回-1
                if (hasQueuedPredecessors())
                    return -1;
                //后面与非公平一样
                int available = getState();
                int remaining = available - acquires;
                if (remaining < 0 ||
                    compareAndSetState(available, remaining))
                    return remaining;
            }
        }

从上面可以看到,FairSync与NonFairSync的区别就在于会首先判断当前队列中有没有线程在等待,如果有,就老老实实进入到等待队列;而不像NonfairSync一样首先试一把,说不定就恰好获得了一个许可,这样就可以插队了。
看完了获取许可后,再看一下释放许可。

释放许可

释放许可也有几个重载方法,但都会调用下面这个带参数的方法,

public void release(int permits) {
        if (permits < 0) throw new IllegalArgumentException();
        sync.releaseShared(permits);
    }

releaseShared方法在AQS中,如下:

public final boolean releaseShared(int arg) {
        //如果改变许可数量成功
        if (tryReleaseShared(arg)) {
            doReleaseShared();
            return true;
        }
        return false;
    }

AQS子类实现共享模式的类需要实现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改变许可数量成功,返回true
                if (compareAndSetState(current, next))
                    return true;
            }
        }

从上面可以看到,一旦CAS改变许可数量成功,那么就会调用doReleaseShared()方法释放阻塞的线程。

减小许可数量

Semaphore还有减小许可数量的方法,该方法可以用于用于当资源用完不能再用时,这时就可以减小许可证。代码如下:

protected void reducePermits(int reduction) {
        if (reduction < 0) throw new IllegalArgumentException();
        sync.reducePermits(reduction);
    }

可以看到,委托给了Sync,Sync的reducePermits方法如下:

  final void reducePermits(int reductions) {
            for (;;) {
                //得到当前剩余许可数量
                int current = getState();
                //得到减完之后的许可数量
                int next = current - reductions;
                if (next > current) // underflow
                    throw new Error("Permit count underflow");
                //如果CAS改变成功
                if (compareAndSetState(current, next))
                    return;
            }
        }

从上面可以看到,就是CAS改变AQS中的state变量,因为该变量代表许可证的数量。

获取剩余许可数量

Semaphore还可以一次将剩余的许可数量全部取走,该方法是drain方法,如下:

public int drainPermits() {
        return sync.drainPermits();
    }

Sync的实现如下:

 final int drainPermits() {
            for (;;) {
                int current = getState();
                if (current == 0 || compareAndSetState(current, 0))
                    return current;
            }
        }

可以看到,就是CAS将许可数量置为0。

总结

Semaphore是信号量,用于管理一组资源。其内部是基于AQS的共享模式,AQS的状态表示许可证的数量,在许可证数量不够时,线程将会被挂起;而一旦有一个线程释放一个资源,那么就有可能重新唤醒等待队列中的线程继续执行。

© 著作权归作者所有

共有 人打赏支持
harries
粉丝 44
博文 99
码字总数 86900
作品 3
朝阳
其他
Semaphore源码分析-java8

1.特性分析 Semaphore就是一个计数的信号量 每一个线程在获取资源前,必须从semaphore获取许可,这保证了一定有可用的资源。 ,这样就保证了acquire方法被调用时,被使用完的资源依然可以放回...

caoxiaohong1005
04/22
0
0
原理剖析(第 006 篇)Semaphore工作原理分析

原理剖析(第 006 篇)Semaphore工作原理分析 - 一、大致介绍 二、简单认识Semaphore 2.1 何为Semaphore? 2.2 Semaphore的state关键词 2.3 常用重要的方法 2.4 设计与实现伪代码 2.5、Semap...

HMILYYLIMH
03/12
0
0
Java并发编程笔记之Semaphore信号量源码分析

JUC 中 Semaphore 的使用与原理分析,Semaphore 也是 Java 中的一个同步器,与 CountDownLatch 和 CycleBarrier 不同在于它内部的计数器是递增的,那么,Semaphore 的内部实现是怎样的呢? ...

狂小白
07/08
0
0
Semaphore CountDownLatch CyclicBarrier 源码分析

java5 中 ,提供了几个并发工具类 ,Semaphore CountDownLatch CyclicBarrier,在并发编程中非常实用。前两者通过 内部类sync 继承AQS,使用共享资源的模式,AQS的实现可参考我的另一篇 AQS ...

ovirtKg
2016/10/19
18
0
【死磕Java并发】-----J.U.C之并发工具类:Semaphore

此篇博客所有源码均来自JDK 1.8 信号量Semaphore是一个控制访问多个共享资源的计数器,和CountDownLatch一样,其本质上是一个“共享锁”。 Semaphore,在API是这么介绍的: 一个计数信号量。...

chenssy
2017/05/03
0
0
使用Semaphore限制资源并发访问的线程数

从JDK 1.5之后,在java.util.concurrent包下引入了好多的处理多线程的工具类,本文将介绍用来控制资源同时访问个数的Semaphore工具类, 然后采用Semaphore给出一个泊车的实例,最后给出Semapho...

王孟君
2016/11/06
919
5
111 多线程JUC包下代码分析

Java多线程系列目录(共43篇) AtomicLongFieldUpdater:通过反射+CAS实现对传入对象的指定long字段实现类似AtomicLong的操作 http://www.cnblogs.com/skywang12345/p/javathreadscategory.ht...

素雷
2017/10/31
0
0
我为什么写了一本书

我为什么写了一本书? 先聊聊我不喜欢看哪些技术书籍? 昨天直播的时候给大家说了一下我写了一本书,很多人问。现在介绍一下。 我有3不看。 没代码的技术书不看。 故弄玄虚的不看。 翻译api...

叶孤城___
2016/06/06
0
0
AbstractQueuedSynchronizer在工具类Semaphore、CountDownLatch、ReentrantLock中的应用和CyclicBarrier

在上篇文章本人粗略地整理了AbstractQueuedSynchronizer和ReentrantLock的源码要点。其实,在java.util.concurrent包中,AbstractQueuedSynchronizer的应用非常广泛,而不局限于在Reentrant...

pczhangtl
2013/11/18
0
0
咱们来聊聊并发工具类Semaphore

什么是Semaphore? Semaphore是计数信号量。Semaphore管理一系列许可证。每个acquire方法阻塞,直到有一个许可证可以获得然后拿走一个许可证;每个release方法增加一个许可证,这可能会释放一...

harries
07/11
0
0

没有更多内容

加载失败,请刷新页面

加载更多

下一页

shell中的函数、shell中的数组、告警系统需求分析

shell中的函数 格式: 格式: function f_name() { command } 函数必须要放在最前面 示例1(用来打印参数) 示例2(用于定义加法) 示例3(用于显示IP) shell中的数组 shell中的数组1 定义数...

Zhouliang6
今天
2
0
用 Scikit-Learn 和 Pandas 学习线性回归

      对于想深入了解线性回归的童鞋,这里给出一个完整的例子,详细学完这个例子,对用scikit-learn来运行线性回归,评估模型不会有什么问题了。 1. 获取数据,定义问题     没有...

wangxuwei
今天
1
0
MAC安装MAVEN

一:下载maven压缩包(Zip或tar可选),解压压缩包 二:打开终端输入:vim ~/.bash_profile(如果找不到该文件新建一个:touch ./bash_profile) 三:输入i 四:输入maven环境变量配置 MAVEN_HO...

WALK_MAN
今天
0
0
33.iptables备份与恢复 firewalld的9个zone以及操作 service的操作

10.19 iptables规则备份和恢复 10.20 firewalld的9个zone 10.21 firewalld关于zone的操作 10.22 firewalld关于service的操作 10.19 iptables规则备份和恢复: ~1. 保存和备份iptables规则 ~2...

王鑫linux
今天
2
0
大数据教程(2.11):keeperalived+nginx高可用集群搭建教程

上一章节博主为大家介绍了目前大型互联网项目的系统架构体系,相信大家应该注意到其中很重要的一块知识nginx技术,在本节博主将为大家分享nginx的相关技术以及配置过程。 一、nginx相关概念 ...

em_aaron
今天
1
0
Apache Directory Studio连接Weblogic内置LDAP

OBIEE默认使用Weblogic内置LDAP管理用户及组。 要整理已存在的用户及组,此前办法是导出安全数据,文本编辑器打开认证文件,使用正则表达式获取用户及组的信息。 后来想到直接用Apache Dire...

wffger
今天
2
0
HFS

FS,它是一种上传文件的软件。 专为个人用户所设计的 HTTP 档案系统 - Http File Server,如果您觉得架设 FTP Server 太麻烦,那么这个软件可以提供您更方便的档案传输系统,下载后无须安装,...

garkey
今天
1
0
Java IO类库之BufferedInputStream

一、BufferedInputStream介绍 /** * A <code>BufferedInputStream</code> adds * functionality to another input stream-namely, * the ability to buffer the input and to * sup......

老韭菜
今天
0
0
STM 32 窗口看门狗

http://bbs.elecfans.com/jishu_805708_1_1.html https://blog.csdn.net/a1985831055/article/details/77404131...

whoisliang
昨天
1
0
Dubbo解析(六)-服务调用

当dubbo消费方和提供方都发布和引用完成后,第四步就是消费方调用提供方。 还是以dubbo的DemoService举例 -- 提供方<dubbo:application name="demo-provider"/><dubbo:registry address="z...

青离
昨天
2
0

没有更多内容

加载失败,请刷新页面

加载更多

下一页

返回顶部
顶部