共享式获得锁
共享式获取锁的顶层函数为acquireShared()
,该函数首先调用tryAcquireShared()
尝试互锁,如果获取不到锁,则将该线程加入到队列中。
public final void acquireShared(int arg) {
//尝试获取锁
//小于0,代表获取锁失败
// 具体的逻辑由子类实现
if (tryAcquireShared(arg) < 0)
doAcquireShared(arg);
}
doAcquireShared()
函数将该线程加入到队列中,然后通过有限次的循环获取锁,并判断是否该阻塞
private void doAcquireShared(int arg) {
//将该线程加入到队列中
final Node node = addWaiter(Node.SHARED);
boolean failed = true;
try {
boolean interrupted = false;
for (;;) {
//获取该线程节点的前趋节点。
final Node p = node.predecessor();
/**
* 如果p为头节点,再次尝试获取锁
* p可能是独占式节点(EXCLUSIVE),也可能是共享式节点(SHARED).
* 独占式节点则不能再获取锁
* 共享式节点可以获取锁,但当资源用尽时也不能获取到锁
* 能不能获取到锁,要看具体tryAcquireShared的实现
**/
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);
}
}
private void setHeadAndPropagate(Node node, int propagate) {
//获取头节点
Node h = head;
//将当前节点设置成头节点
setHead(node);
if (propagate > 0 || h == null || h.waitStatus < 0 ||
(h = head) == null || h.waitStatus < 0) {
Node s = node.next;
//如果s是共享节点,则唤醒共享节点
if (s == null || s.isShared())
doReleaseShared();
}
}
private void doReleaseShared() {
/*
* 下面的循环在 head 节点存在后继节点的情况下,做了两件事情:
* 1. 如果 head 节点等待状态为 SIGNAL,则将 head 节点状态设为 0,并唤醒后继节点
* 2. 如果 head 节点等待状态为 0,则将 head 节点状态设为 PROPAGATE,保证唤醒能够正
* 常传播下去。关于 PROPAGATE 状态的细节分析,后面会讲到。
*/
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;
}
}
共享式释放锁
public final boolean releaseShared(int arg) {
if (tryReleaseShared(arg)) {
doReleaseShared();
return true;
}
return false;
}