Java并发工具类Semaphore源码剖析(转载)

本篇文章的主要内容:

1:举例说明Semaphore
2:Semaphore的原理剖析3:Semaphore的源码剖析

1:举例说明Semaphore

public class SemaphoreTest {
 private static Semaphore sh = new Semaphore(3); public static void main(String[] args) { for (int i = 0; i < 10; i++) { int finalI = i; new Thread(() -> { try { sh.acquire(); String dateStr = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date()); System.out.println("Thread-" + (finalI + 1) + "获取运行权限:" + dateStr); Thread.sleep(1000); sh.release(); } catch (Exception e) { e.printStackTrace(); } }).start(); } }}

运行结果如下图:

Java并发工具类Semaphore源码剖析

 

通过上面的运行结果,会发现一个规律,那就是一次最多有三个线程同时运行,在看上面的demo,我们在创建Semaphore的时候传递了参数3,这两者似乎存在着联系,我们可以大胆的猜想。

创建Semaphore对象传递的参数决定了多少个线程能够同时获取资源,然后执行代码逻辑。

我们对Java并发包中的AQS框架非常的熟悉了,这个类的原理似乎和AQS中共享资源的获取和释放非常的类似,我们首先回顾一下这个知识点。

1:state<0:表示获取资源失败
2:state==0:表示获取资源成功,但是没有剩余的资源了3:state>0:表示获取资源成功,并且还有剩余的资源

在创建Semaphore时传递的参数就是state,我们传递3,说明同时能够有3个线程获取到资源,其他线程需要等待,直到其他线程释放资源。

2:Semaphore的原理剖析

通过上面的demo,大家是否对Semaphore的用法有所了解了,它的内部原理其实就是AQS中的共享模式下对资源的获取和释放

1:创建Semaphore时传递的参数,底层赋值给了AQS的共享资源state
2:Semaphore中的acquire()方法其实就是尝试获取资源,如果还有剩余的资源,则代表获取了锁,就可以继续执行了,如果没有剩余的资源,那么这个线程就会放入CLH等待队列,等待有资源。3:Semaphore中的release()方法就是释放资源。

通过上面的介绍,对Semaphore总结一句话:同一个时间允许多少个线程获取到共享资源,获取到资源的线程能够继续执行代码逻辑,没有获取到资源的线程则需要等待了,直到有共享资源或者被其他线程中断。

结合上面的例子,开始进行详细的分析,可能上面的demo执行结果有所不同,因为多线程下,哪个线程获取到CPU,哪个线程首先执行,但是相同的就是同一时间最多有3个线程同时执行。

1:Thread2首先获取到了CPU的执行权,此时共享资源是3,所以Thread2获取到了资源,并且将资源数量-1,此时共享资源是2
2:Thread3获取到了CPU的执行权,此时共享资源是2,所以Thread3获取到了资源,并且将资源数量-1,此时共享资源是13:Thread1获取到了CPU的执行权,此时共享资源是1,所以Thread1获取到了资源,并且将资源数量-1,此时共享资源0.4:Thread5获取到了CPU的执行权,此时共享资源是0,所以Thread5获取资源失败,那么Thread5就是封装成Node节点存放到CLH等待队列中。5:下面的线程以此类推。

上面的步骤仅仅是一种情况,可能Thread5获取CPU后,Thread2已经释放了资源,但是不管怎样,因为只有3个共享资源,所以同时只能有3个线程获取。

接下来我们从源码角度来看看Semaphore是否像我们上面所说的。

3:Semaphore的源码剖析

通过前几篇文章的学习,我们知道,如果利用AQS框架来实现并发功能,那一定有一个内部类,并且这个内部类继承了AQS,我们搜索Semaphore源码。

Java并发工具类Semaphore源码剖析

 

上图中的Sync就是AQS的子类,其中NonfairSync和FairSync是Sync的两个子类,分别对应的是非公平模式和公平模式。

3.1:Semaphore的构造函数

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

第一个参数:permits:表示初始化的共享资源

第二个参数:fair:true:表示公平模式,false:表示非公平模式,默认是非公平模式。

这两个构造函数底层要么调用非公平模式(NonfairSync),要么调用公平模式(FairSync),然后把permits传递进去,那么我们进入NonfairSync

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

从上面的代码可以看到Semaphore构造函数最终调用的是Sync的构造函数,而Sync直接调用AQS的setState()方法,将permits赋值给了AQS的共享资源state。

3.2:acquire()方法

public void acquire() throws InterruptedException {
 sync.acquireSharedInterruptibly(1);}public final void acquireSharedInterruptibly(int arg) throws InterruptedException { if (Thread.interrupted()) throw new InterruptedException(); if (tryAcquireShared(arg) < 0) doAcquireSharedInterruptibly(arg);}

上面的acquire()方法底层直接调用AQS中的acquireSharedInterruptibly()方法,而这个方法是AQS中共享模式下获取资源的模板方法,而AQS的子类必须实现tryAcquireShared()方法,那么我们接下来到Sync中看看这个方法怎样实现的。以非公平类为例:NonfairSync

final int nonfairTryAcquireShared(int acquires) {
 for (;;) { int available = getState(); int remaining = available - acquires; if (remaining < 0 || compareAndSetState(available, remaining)) return remaining; }}

首先是一个for无限循环,然后通过调用AQS的getState()方法获取到可用的共享资源state,然后减去这一次所需要的共享资源,获取到剩余的remaining,然后有两个结果

第一个结果:如果剩余资源remaining<0:说明剩余的资源不足,获取资源失败
第二个结果,如果剩余资源remaining>=0:说明剩余的资源能够满足,获取资源成功,只是当remaining==0时没有剩余的资源了,remaining>0时还有剩余的资源。

Java并发工具类Semaphore源码剖析

 

3.3:release()方法

public void release() {
 sync.releaseShared(1);}public final boolean releaseShared(int arg) { if (tryReleaseShared(arg)) { doReleaseShared(); return true; } return false;}

release()方法底层直接调用了AQS的releaseShared()方法进行释放资源,而这个方法也是一个模板方法,AQS的子类只需要实现tryReleaseShared()即可,那么我们接下来看看Sync是怎样实现这个方法的。

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"); if (compareAndSetState(current, next)) return true; }}

这个方法首先调用AQS的getState()方法获取到共享资源state,然后联合此次释放的资源releases,通过CAS机制更新state,直到更新成功才返回。

Java并发工具类Semaphore源码剖析

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值