计数信号量用来控制同时访问某个特定资源的操作数量,或者同时执行某个指定操作的数量。计数信号量还可以用来实现某种资源池,或者对容器施加边界,如数据库连接池,固定大小的数据集。
Semaphore 管理一组虚拟的许可,在执行操作的时可以先获得许可,并在使用以后释放许可。如果没有许可,那么 acquire 将阻塞直到有许可,release 方法返回一个可用信号量。Semaphore 内部是通过 AQS 来实现的。
Semaphore 主要实现方法:
- Semaphore 构造函数,传入信号数目,Semaphore 将信号数目设置到 AQS 的 state 上;
- acquire 获取信号,如果信号数目大于0,通过 CAS 将信号数减1, 获取成功;否则被阻塞,并将线程记录到 AQS 的等待锁队列中;
- release 释放信号,通过 CAS 将信号数加1,并唤醒等待队列上队首线程重新尝试获取信号。
Semapore 结构:
public class Semaphore implements Serializable {
private static final long serialVersionUID = -3222578661600680210L;
private final Semaphore.Sync sync;
public Semaphore(int var1) {
this.sync = new Semaphore.NonfairSync(var1);
}
public Semaphore(int var1, boolean var2) {
this.sync = (Semaphore.Sync)(var2?new Semaphore.FairSync(var1):new Semaphore.NonfairSync(var1));
}
public void acquire() throws InterruptedException {
this.sync.acquireSharedInterruptibly(1);
}
public void acquireUninterruptibly() {
this.sync.acquireShared(1);
}
public boolean tryAcquire() {
return this.sync.nonfairTryAcquireShared(1) >= 0;
}
public boolean tryAcquire(long var1, TimeUnit var3) throws InterruptedException {
return this.sync.tryAcquireSharedNanos(1, var3.toNanos(var1));
}
public void release() {
this.sync.releaseShared(1);
}
public void acquire(int var1) throws InterruptedException {
if(var1 < 0) {
throw new IllegalArgumentException();
} else {
this.sync.acquireSharedInterruptibly(var1);
}
}
public void acquireUninterruptibly(int var1) {
if(var1 < 0) {
throw new IllegalArgumentException();
} else {
this.sync.acquireShared(var1);
}
}
public boolean tryAcquire(int var1) {
if(var1 < 0) {
throw new IllegalArgumentException();
} else {
return this.sync.nonfairTryAcquireShared(var1) >= 0;
}
}
public boolean tryAcquire(int var1, long var2, TimeUnit var4) throws InterruptedException {
if(var1 < 0) {
throw new IllegalArgumentException();
} else {
return this.sync.tryAcquireSharedNanos(var1, var4.toNanos(var2));
}
}
public void release(int var1) {
if(var1 < 0) {
throw new IllegalArgumentException();
} else {
this.sync.releaseShared(var1);
}
}
}
从 Semaphore 中我们可以看到 Semaphore 的锁是通过 Sync 这个类完成的,Sync 则继承自 AQS ,AQS 是独占锁和共享锁的父类,通过继承 AQS 实现共享锁。
Sync 有两个子类是 FairSync 和 NonfairSync,分别代表公平锁和非公平锁。Semaphore 默认是非公平锁。为什么使用非公平锁,这个是性能上的考虑,如果每次都去唤醒线程去获取信号,这是非常消耗资源的,非公平锁的性能和吞吐量也明显优于公平锁。
FairSync 结构:
static final class FairSync extends Semaphore.Sync {
private static final long serialVersionUID = 2014338818796000944L;
FairSync(int var1) {
super(var1);
}
protected int tryAcquireShared(int var1) {
int var2;
int var3;
do {
if(this.hasQueuedPredecessors()) {
return -1;
}
var2 = this.getState();
var3 = var2 - var1;
} while(var3 >= 0 && !this.compareAndSetState(var2, var3));
return var3;
}
}
FairSync 判断是否有线程等待,如果没有则尝试获取信号,如果有则加入到等待队列。
NonfariSync 结构:
static final class NonfairSync extends Semaphore.Sync {
private static final long serialVersionUID = -2694183684443567898L;
NonfairSync(int var1) {
super(var1);
}
protected int tryAcquireShared(int var1) {
return this.nonfairTryAcquireShared(var1);
}
}
abstract static class Sync extends AbstractQueuedSynchronizer {
private static final long serialVersionUID = 1192457210091910933L;
Sync(int var1) {
this.setState(var1);
}
final int getPermits() {
return this.getState();
}
final int nonfairTryAcquireShared(int var1) {
int var2;
int var3;
do {
var2 = this.getState();
var3 = var2 - var1;
} while(var3 >= 0 && !this.compareAndSetState(var2, var3));
return var3;
}
protected final boolean tryReleaseShared(int var1) {
int var2;
int var3;
do {
var2 = this.getState();
var3 = var2 + var1;
if(var3 < var2) {
throw new Error("Maximum permit count exceeded");
}
} while(!this.compareAndSetState(var2, var3));
return true;
}
final void reducePermits(int var1) {
int var2;
int var3;
do {
var2 = this.getState();
var3 = var2 - var1;
if(var3 > var2) {
throw new Error("Permit count underflow");
}
} while(!this.compareAndSetState(var2, var3));
}
final int drainPermits() {
int var1;
do {
var1 = this.getState();
} while(var1 != 0 && !this.compareAndSetState(var1, 0));
return var1;
}
}
NonfairSync 则先尝试获取锁,如果获取失败则再加入等待队列。
AQS 结构:
public abstract class AbstractQueuedSynchronizer extends AbstractOwnableSynchronizer implements Serializable {
private static final long serialVersionUID = 7373984972572414691L;
private transient volatile AbstractQueuedSynchronizer.Node head;
private transient volatile AbstractQueuedSynchronizer.Node tail;
private volatile int state;
static final long spinForTimeoutThreshold = 1000L;
private static final Unsafe unsafe = Unsafe.getUnsafe();
private static final long stateOffset;
private static final long headOffset;
private static final long tailOffset;
private static final long waitStatusOffset;
private static final long nextOffset;
protected AbstractQueuedSynchronizer() {
}
public final void acquireSharedInterruptibly(int arg)
throws InterruptedException {
if (Thread.interrupted())
throw new InterruptedException();
if (tryAcquireShared(arg) < 0)
doAcquireSharedInterruptibly(arg);
}
// 获取失败会被阻塞
private void doAcquireSharedInterruptibly(int arg)
throws InterruptedException {
final Node node = addWaiter(Node.SHARED);
boolean failed = true;
try {
for (;;) {
final Node p = node.predecessor();
if (p == head) {
int r = tryAcquireShared(arg);
if (r >= 0) {
// 设置等待线程头部,并传播唤醒下一个头部等待线程
setHeadAndPropagate(node, r);
p.next = null; // help GC
failed = false;
return;
}
}
if (shouldParkAfterFailedAcquire(p, node) &&
parkAndCheckInterrupt())
throw new InterruptedException();
}
} finally {
if (failed)
cancelAcquire(node);
}
}
// 唤醒等待线程队首线程
private void doReleaseShared() {
/*
* Ensure that a release propagates, even if there are other
* in-progress acquires/releases. This proceeds in the usual
* way of trying to unparkSuccessor of head if it needs
* signal. But if it does not, status is set to PROPAGATE to
* ensure that upon release, propagation continues.
* Additionally, we must loop in case a new node is added
* while we are doing this. Also, unlike other uses of
* unparkSuccessor, we need to know if CAS to reset status
* fails, if so rechecking.
*/
for (;;) {
Node h = head;
if (h != null && h != tail) {
int ws = h.waitStatus;
if (ws == Node.SIGNAL) {
if (!compareAndSetWaitStatus(h, Node.SIGNAL, 0))
continue; // loop to recheck cases
unparkSuccessor(h);
}
else if (ws == 0 &&
!compareAndSetWaitStatus(h, 0, Node.PROPAGATE))
continue; // loop on failed CAS
}
if (h == head) // loop if head changed
break;
}
}
private void doReleaseShared() {
while(true) {
AbstractQueuedSynchronizer.Node var1 = this.head;
if(var1 != null && var1 != this.tail) {
int var2 = var1.waitStatus;
if(var2 == -1) {
if(!compareAndSetWaitStatus(var1, -1, 0)) {
continue;
}
this.unparkSuccessor(var1);
} else if(var2 == 0 && !compareAndSetWaitStatus(var1, 0, -3)) {
continue;
}
}
if(var1 == this.head) {
return;
}
}
}
}
Semaphore 调用 acquire方法时,调用 AQS 的 acquireSharedInterruptibly 方法,AQS 则调用子类的 tryAcquireShared 方法,如果获取成功,则直接返回;如果获取失败,则调用调用 AQS 的方法 doAcquireSharedInterruptibly 阻塞并加入到等待队列等待唤醒。
Semaphore 调用 release 方法时,调用 AQS 的 releaseShared 方法,AQS 则调用子类的 tryReleaseShared 方法,如果释放成功,则调用 doReleaseShared 方法唤醒等待队列队首线程,线程启动后,如果 tryAcquireShared 返回值大于等于 0,则通过 setHeadAndPropagate 方法进行传播,唤醒下一个线程。