先看到Semaphore的构造方法
//该方法默认非公平锁
public Semaphore(int permits) {
sync = new NonfairSync(permits);
}
public Semaphore(int permits, boolean fair) {
sync = fair ? new FairSync(permits) : new NonfairSync(permits);
}
看到Semaphore的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);
}
tryAcquireShared方法
//FairSync公平锁的实现
protected int tryAcquireShared(int acquires) {
//死循环
for (;;) {
//判断队列中是否有线程在排队等待
if (hasQueuedPredecessors())
//有其他线程在排队,则获取锁失败
return -1;
//获取state的值
int available = getState();
//获取剩余值
int remaining = available - acquires;
//如果剩余值小于0 则 返回剩余值, 表示获取锁失败
//如果剩余值大于等于0 , 则cas修改state的值,cas成功,返回剩余值,获得锁成功
//cas失败则下一次循环
if (remaining < 0 ||
compareAndSetState(available, remaining))
return remaining;
}
}
//NonfairSync非公平锁的实现
final int nonfairTryAcquireShared(int acquires) {
//对比上面的公平锁,不会判断有没有线程在排队。
for (;;) {
int available = getState();
int remaining = available - acquires;
if (remaining < 0 ||
compareAndSetState(available, remaining))
return remaining;
}
}
doAcquireSharedInterruptibly方法
//doAcquireShared方法之前文章讲过的代码了,不做赘述了,这里的doAcquireSharedInterruptibly只是会响应中断,抛出中断异常,其他的是一样的。我们针对Semaphore分析r为0的情况,之前的解析的读写锁返回的r只能是-1和1
private void doAcquireSharedInterruptibly(int arg)
throws InterruptedException {
//将线程包装为node节点,进入等待队列,修改前置节点的waitStatus,休眠前如果前置节点是头结点,尝试抢锁
final Node node = addWaiter(Node.SHARED);
boolean failed = true;
try {
for (;;) {
final Node p = node.predecessor();
if (p == head) {
//这里r返回的是扣减后的state的值,可以是0
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);
}
}
setHeadAndPropagate方法也在读写锁的文章讲过了,这里再借着Semaphore看下Node.PROPAGATE
private void setHeadAndPropagate(Node node, int propagate) {
//先获取head
Node h = head;
setHead(node);
//这里propagate可能为0
//在判断头结点的waitStatus , 这里头结点的状态很可能为0, 唤醒了node 把自己的waitStatus 修改成了0
//(h = head) == null 重新获取头结点(可能是node节点也可能不是),此时该节点的waitStatus可能不是0(还有其他节点再node后面排队),那就执行if里面的代码。
//也可能node节点的waitStatus也是0,但是有可能队列中是有其他共享节点排队的
//比如另一个线程(叫做B线程)执行了addWaiter进入队列,还没来得及执行shouldParkAfterFailedAcquire修改头节点(node)的waitStatus为-1 , 此时这里的doReleaseShared方法就不会被调用了。
//接着B线程正常执行 ,然后有A线程释放了资源(此时A线程在执行releaseShared的doReleaseShared方法),但是此时来了C线程修改state成功了抢到了锁,然后B线程修改头结点的waitStatus为-1,然后再次抢锁,发现此时state为0,B线程去阻塞
if (propagate > 0 || h == null || h.waitStatus < 0 ||
//重新获取一次头节点(可能是node节点也可能不是)
(h = head) == null || h.waitStatus < 0) {
Node s = node.next;
if (s == null || s.isShared())
doReleaseShared();
}
}
看到Semaphore的release方法
public void release() {
sync.releaseShared(1);
}
public final boolean releaseShared(int arg) {
//恢复state的值
if (tryReleaseShared(arg)) {
//唤醒节点
doReleaseShared();
return true;
}
return false;
}
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恢复state的值,死循环,直到成功,返回true
if (compareAndSetState(current, next))
return true;
}
}
doReleaseShared方法也在读写锁的文章讲过了,这里再借着Semaphore看下Node.PROPAGATE
private void doReleaseShared() {
//1---A线程执行下面方法
for (;;) {
//1---获取头结点
Node h = head;
if (h != null && h != tail) {
//1---假设此时B线程还没来得及修改头节点的waitStatus从0更新为-1
int ws = h.waitStatus;
//2---第二次循环A线程获取头结点状态为SIGNAL(-1)
if (ws == Node.SIGNAL) {
//2---cas修改头结点为0
if (!compareAndSetWaitStatus(h, Node.SIGNAL, 0))
continue; // loop to recheck cases
//2---cas成功则唤醒B线程,此时B线程醒来去获取锁,实际上如果state没有被其他线程释放回复,B线程也不一定能抢到锁。
//这么看来PROPAGATE作用也就是让doReleaseShared方法更有机会去唤醒睡眠的线程,使得排队的线程在允许的情况下更早被唤醒。
unparkSuccessor(h);
}
//1---则走了这里,cas修改头结点的waitStatus为-3,但是被B线程抢先修改成了-1,然后B线程睡眠了
//1---此时cas失败,然后进入下一次循环
else if (ws == 0 &&
!compareAndSetWaitStatus(h, 0, Node.PROPAGATE))
//从网上搜索其他文章可以看到说Node.PROPAGATE可以防止队列中有线程不被唤醒, 实际上在jdk8的代码来说 setHeadAndPropagate方法在旧header的waitStatus状态不小于0的情况下,会重新获取新的header的waitStatus再进行判断。并且(shouldParkAfterFailedAcquire方法)进入队列准备去排队的节点第一次修改前置节点的waitStatus为-1时,不会去睡眠,还会再去尝试抢锁,获取不到再去睡眠(此时前置节点的waitStatus一定为-1),不会存在队列中有线程不被唤醒的情况。
continue; // loop on failed CAS
}
if (h == head) // loop if head changed
break;
}
}
到此Semaphore源码也就解析完了,其实主要是来看Node.PROPAGAT的QAQ