一、类图
很简单,内部持有实现了AbstractQueuedSynchronizer的Sync,Sync为Semaphore的内部类,它没有什么特殊的成员变量,实现了基本的获取许可与释放的逻辑
二、许可的获取与释放
2.1 获取许可
//非公平锁
final int java.util.concurrent.Semaphore.Sync#nonfairTryAcquireShared(int acquires) {
for (;;) {
//获取可用的许可个数
int available = getState();
//减去需要请求的许可数
int remaining = available - acquires;
//如果许可小于零,那么认为获取许可失败,如果是正数,那么进行CAS改值
if (remaining < 0 ||
compareAndSetState(available, remaining))
return remaining;
}
}
//公平锁
protected int java.util.concurrent.Semaphore.FairSync#tryAcquireShared(int acquires) {
for (;;) {
//先判断队列中是否存在其他waiting的节点,如果有,判定线程获取许可失败,保证公平性
if (hasQueuedPredecessors())
return -1;
//获取可用的许可个数
int available = getState();
//减去需要请求的许可数
int remaining = available - acquires;
//如果许可小于零,那么认为获取许可失败,如果是正数,那么进行CAS改值
if (remaining < 0 ||
compareAndSetState(available, remaining))
return remaining;
}
}
当我们创建信号量的时候会设置一定数量的许可,线程获取到一个许可便会将许可总数减去1,然后返回剩余的许可数量,如果剩余的许可数量非负数,那么表示获取许可成功。
如果获取许可失败,需要加入到同步队列中等待其他线程释放许可唤醒自己
private void java.util.concurrent.locks.AbstractQueuedSynchronizer#doAcquireSharedInterruptibly(int arg)
throws InterruptedException {
//CAS添加节点到同步队列,具体的添加过程,请移步到第5节,此处不再赘述
//Node.SHARED用于表示当前线程所处模式为共享模式,是来竞争共享锁的
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;
}
}
//前驱节点必须为SIGNAL才允许挂起线程,如果不是SIGNAL,是CANCEL,那么先去掉当前节点连续的前驱CANCEL节点
//如果是其他的状态,比如0或者传播状态,那么先将前驱节点设置为SIGNAL,然后再尝试竞争许可,具体逻辑与这么做的原因
//请移步到第5节JDK并发锁之AQS与独占锁
if (shouldParkAfterFailedAcquire(p, node) &&
//挂起
parkAndCheckInterrupt())
//如果发生中断,将抛出中断异常
throw new InterruptedException();
}
} finally {
//如果许可获取失败(通常在设置有超时时间获取许可的方法中可能会导致failed = true),CANCEL节点,具体逻辑请移步第5节JDK并发锁之AQS与独占锁
if (failed)
cancelAcquire(node);
}
}
2.1 释放许可
protected final boolean java.util.concurrent.Semaphore.Sync#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;
}
}
许可的释放仅仅是将原来线程占用的许可还回去即可
三、总结
只要学习了第6节点读写锁,理解Semaphore是非常简单,所以对于Semaphore不会花太多的重复篇幅去分析。