首先来回顾一下AQS Node的主要结构,对于节点状态PROPAGATE的无条件传播,可能不理解。看完doAcquireShared的源码就会知道了
static final class Node {
// 标识节点正在共享模式下
static final Node SHARED = new Node();
// 标识节点正在独占模式下
static final Node EXCLUSIVE = null;
// 标识该节点的动作被取消
static final int CANCELLED = 1;
// 标识后续节点需要被唤醒
static final int SIGNAL = -1;
// 标识节点正在等待某些条件(ConditionObject)
static final int CONDITION = -2;
// 标识下一次的acquireShared,应该无条件的传播(唤醒共享节点)
static final int PROPAGATE = -3;
}
共享模式获取锁
public final void acquireShared(int arg) {
// 获取锁失败,加入同步队列,并再次获取锁
// tryAcquireShared需要实现类重写该方法
if (tryAcquireShared(arg) < 0)
doAcquireShared(arg);
}
tryAcquireShared的返回值,负数表示失败;0表示成功,但没有剩余可用资源;正数表示成功,且有剩余资源。
private void doAcquireShared(int arg) {
// 加入同步等待队列
final Node node = addWaiter(Node.SHARED);
boolean failed = true;
try {
boolean interrupted = false;
for (;;) {
// 前置节点如果是头节点的话,那么再次尝试获取锁
final Node p = node.predecessor();
if (p == head) {
int r = tryAcquireShared(arg);
// 获取锁成功
if (r >= 0) {
// 当前节点获取锁成功了,将该节点设置为头节点,并唤醒后继共享节点
setHeadAndPropagate(node, r);
p.next = null; // help GC
if (interrupted)
selfInterrupt();
failed = false;
return;
}
}
if (shouldParkAfterFailedAcquire(p, node) &&
parkAndCheckInterrupt())
interrupted = true;
}
} finally {
if (failed)
cancelAcquire(node);
}
}
为啥获取锁成功了,还要唤醒后继的共享节点?
共享锁是可以多个线程共有的,当一个节点的线程获取共享锁后,必然要通知后继共享节点的线程,也可以获取锁了,这样就不会让其他等待的线程等很久,而传播性的目的也是尽快通知其他等待的线程尽快获取锁。
共享模式释放锁
public final boolean releaseShared(int arg) {
// tryReleaseShared需要子类实现
if (tryReleaseShared(arg)) {
// 释放锁成功后唤醒后继节点
doReleaseShared();
return true;
}
return false;
}
private void doReleaseShared() {
for (;;) {
Node h = head; // 暂存一下head节点到h
// 如果有头节点,并且队列中不止头节点一个节点
if (h != null && h != tail) {
// 头节点的waitStatus是-1,唤醒后继节点,并且把状态更新成0
int ws = h.waitStatus;
if (ws == Node.SIGNAL) {
if (!compareAndSetWaitStatus(h, Node.SIGNAL, 0))
continue; // loop to recheck cases
unparkSuccessor(h);
}
// 节点的waitStatus已经是0了,说明已经唤醒过后续节点了,那么把他更新成PROPAGATE,下一次无条件传播
else if (ws == 0 &&
!compareAndSetWaitStatus(h, 0, Node.PROPAGATE))
continue; // loop on failed CAS
}
// 如果头节点没有变化,那么退出
if (h == head) // loop if head changed
break;
}
}
从这里的逻辑可以看出,如果节点的waitStatus是-3,也就是PROPAGATE,会直接进入到下次循环重新拿头节点的状态,唤醒后续共享节点。这也就是我理解的无条件传播