ReentrantReadWriteLock之唤醒AQS中读锁流程
tryAcquireShared方法是尝试获取锁,获取失败返回-1,成功返回1,不会在失败的时候将节点放到同步队列中排队。失败后的流程在doAcquireShared方法中执行。
流程概述:
- 封装当前线程为共享锁节点,并放入同步队列中
- 拿到当前节点的前一个节点,判断是否为head
- 如果是,尝试获取读锁资源,判断是否获取成功
- 如果获取成功,判断当前节点的下一个节点是否是读线程,如果是则唤醒,
- 并且循环执行,直到下一个节点是写线程为止
- 如果获取失败,线程被挂起
- 如果当前节点的前一个节点不是head,线程也是被挂起
private void doAcquireShared(int arg) {
// 封装共享锁Node节点
final Node node = addWaiter(Node.SHARED);
boolean failed = true;
try {
boolean interrupted = false;
for (;;) {
// 拿到当前节点的前一个节点
final Node p = node.predecessor();
if (p == head) {
// 前一个节点是head节点,尝试获取读锁资源
int r = tryAcquireShared(arg);
if (r >= 0) {
// 获取读锁资源成功
// setHeadAndPropagate方法是检查当前节点后面一个节点是否是读线程
// 如果是读线程,同样要唤醒
// 而且会将当前节点设置成头节点
setHeadAndPropagate(node, r);
p.next = null;
if (interrupted)
selfInterrupt();
failed = false;
return;
}
}
// 挂起线程
if (shouldParkAfterFailedAcquire(p, node) &&
parkAndCheckInterrupt())
interrupted = true;
}
} finally {
if (failed)
cancelAcquire(node);
}
}
private void setHeadAndPropagate(Node node, int propagate) {
Node h = head;
setHead(node);
// propagate > 0这个判断条件是为了解决JDK1.5的BUG
// 进入这个方法,入参propagate一定是大于0的
if (propagate > 0 || h == null || h.waitStatus < 0 ||
(h = head) == null || h.waitStatus < 0) {
// 当前线程拿到读锁,必会进入这里
Node s = node.next;
if (s == null || s.isShared())
// 如果下一个节点是读线程,则唤醒
doReleaseShared();
}
}
private void doReleaseShared() {
for (;;) {
Node h = head;
if (h != null && h != tail) {
int ws = h.waitStatus;
if (ws == Node.SIGNAL) {
if (!compareAndSetWaitStatus(h, Node.SIGNAL, 0))
continue;
unparkSuccessor(h);
}
else if (ws == 0 &&
!compareAndSetWaitStatus(h, 0, Node.PROPAGATE))
continue;
}
if (h == head)
break;
}
}