Semaphore(二)

Semaphore(二)

了解Semaphore - 了解方法

源码浅析

方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
// 主要方法
public void acquire() throws InterruptedException {...}

public void acquire(int permits) throws InterruptedException {...}

public void acquireUninterruptibly() {...}

public void acquireUninterruptibly(int permits) {...}

public boolean tryAcquire() {...}

public boolean tryAcquire(long timeout, TimeUnit unit) throws InterruptedException {...}

public void release() {...}

public void release(int permits) {...}

protected void reducePermits(int reduction) {...}

// 其他方法
public boolean isFair() {...}

public int availablePermits() {...}

public int drainPermits() {...}

public final boolean hasQueuedThreads() {...}

protected Collection<Thread> getQueuedThreads() {...}

public final int getQueueLength() {...}

acquire()

acquire()是从Semaphore中获取一个许可证,如果没有可用的许可证的话,将会阻塞,直到有为止。或者在等待的过程中被中断了,也停止阻塞了。(也就是说是可以被中断的,继续看下去会看到不会被中断的acquire() )

1
2
3
4
5
// Semaphore类中
public void acquire() throws InterruptedException {
// 默认获取一个
sync.acquireSharedInterruptibly(1);
}

acquireSharedInterruptibly():

1
2
3
4
5
6
7
8
// AQS中
public final void acquireSharedInterruptibly(int arg)
throws InterruptedException {
if (Thread.interrupted())
throw new InterruptedException();
if (tryAcquireShared(arg) < 0)
doAcquireSharedInterruptibly(arg);
}

从AQS的方法中,我们看到了tryAcquireShared()这个方法。这个方法将会被其子类重写,也就是说执行到AQS这里的时候,就是体现java多态的时候。在Semaphore的FairSync,NonfairSync都继承了AQS,这里将会由一开始的 boolean fair参数去觉得Semaphore的Sync是哪个,如果是FairSync,就执行FairSync的tryAcquireShared方法。否则就执行NonfairSync的方法。

acquireUninterruptily()

从Semaphore中获取许可证,有可用许可证之前,将其堵塞。如果在等待过程中被中断,不会抛出异常,而是继续等待,不会发生中断,但是相比于没有发生中断而获取到许可证的时间,其在发生中断情况获取许可证的时间会有变化(应该是晚了)。当该方法获取了许可证,完成返回之后,将设置中毒呢状态。

1
2
3
4
// Semaphore中
public void acquireUninterruptibly() {
sync.acquireShared(1);
}
1
2
3
4
5
// AQS中
public final void acquireShared(int arg) {
if (tryAcquireShared(arg) < 0)
doAcquireShared(arg);
}

tryAcquire()

1
2
3
4
// Semaphore	
public boolean tryAcquire() {
return sync.nonfairTryAcquireShared(1) >= 0;
}
1
2
3
4
5
6
7
8
9
final int nonfairTryAcquireShared(int acquires) {
for (;;) {
int available = getState();
int remaining = available - acquires;
if (remaining < 0 ||
compareAndSetState(available, remaining))
return remaining;
}
}

从源码可以看到,其会尝试获取1个可用的许可证。也就是如果Semaphore中有1个以上的许可证时,该方法将获取1个许可证并立即返回true,该许可证已经获取到手了。如果没有,那么该方法立即返回false。该方法不是探测有没有信号量,而是有的话给你拿到手,并且返回true,没有的话什么也不干,直接返回false

在前面也提到了tryAcquire()无视什么fair不fair的。只要有就给你一个,才不会等你呢,直接插队,返回一个。在源码中也是直接体现在使用AQS的nonfairTryAcquireShared()方法。默认用不公平的策略

当然也可以使用tryAcquire去遵守公平策略,但这是另一个相似的方法tryAcquire(long timeout, TimeUnit unit):具体用法:tryAcquire(0, TimeUnit.SECONDS),该方法只要有许可证就返回,没有就返回false,继续等待。

tryAcquire(long timeout, TimeUnit unit)

如果在给定的时间timeout中,Semaphore有可用的许可证并且没有中断,那么将从Semaphore中获取一个许可证,并立即返回true。

如果在给定的时间中,没有可用的许可证,将会阻塞等待,直到

  • Semaphore有可用的许可证,并且该线程将会是在队列中下一个获取许可证的线程,此时获取许可证,返回true。
  • 超过了指定时间,此时将会返回false
  • 被中断,抛出异常。

如果timeout小于或者等于0,则该方法不会等待,而是看看有没有可用的许可证,没有的就返回false,有就获取同时返回true。

1
2
3
4
public boolean tryAcquire(long timeout, TimeUnit unit)
throws InterruptedException {
return sync.tryAcquireSharedNanos(1, unit.toNanos(timeout));
}
1
2
3
4
5
6
7
public final boolean tryAcquireSharedNanos(int arg, long nanosTimeout)
throws InterruptedException {
if (Thread.interrupted())
throw new InterruptedException();
return tryAcquireShared(arg) >= 0 ||
doAcquireSharedNanos(arg, nanosTimeout);
}

相比于默认使用不公平策略,丝毫没有公平可言的tryAcquire()而言,tryAcquire(long timeout, TimeUnit unit)还是有公平可言的。其中调用了AQS的方法,而AQS方法里tryAcquireShared()可由FairSync和NonfairSync决定执行。

在之前tryAcquire()中,与tryAcquire()等效的公平设置tryAcquire(0, TimeUnit.SECONDS)中,timeout设置为0,意味着不会等待,而是一调用,如果有,就获取,返回true;如果没有,就返回false。跟tryAcquire()基本一致,是tryAquire()的公平版本。

release()

释放一个许可证,返回给信号量

1
2
3
public void release() {
sync.releaseShared(1);
}
1
2
3
4
5
6
7
public final boolean releaseShared(int arg) {
if (tryReleaseShared(arg)) {
doReleaseShared(); // 里面是CAS操作以及节点的具体操作细节
return true;
}
return false;
}

其他方法

上面是比较重要的几个方法,主要方法中还有几个,不过跟上面讲的基本一样,就是有的不再获取一个,可以获取用户指定数目的许可证数量。还有的可以直接减少许可证的数量。

而在其他方法中,可以简略地梳理一下:

  • int availablePermits():返回此Semaphore中当前可用的许可证数量
  • int drainPermits():获取并返回立即可用的所有许可证
  • boolean isFair():判断是否是公平策略
  • int getQueueLength():返回正在等待获取许可证的线程估计数目
  • protected Collection < Thread >:返回可能等待获取的线程集合。
  • boolean hasQueuedThreads():查询是否有线程正在等待获取。

未完待续…

-------------本文结束感谢您的阅读-------------