前面分析了AbstractQueuedSynchronizer实现的其他两部分:
Condition源码解析
独占模式解析
本文继续介绍AbstractQueuedSynchronizer最后一部分功能--共享模式
共享模式资源获取
共享模式获取资源的入口如下
// 忽略中断异常
public final void acquireShared(int arg) {
if (tryAcquireShared(arg) < 0)
doAcquireShared(arg);
}
// 抛出中断异常
public final void acquireSharedInterruptibly(int arg)
throws InterruptedException {
if (Thread.interrupted())
throw new InterruptedException();
if (tryAcquireShared(arg) < 0)
doAcquireSharedInterruptibly(arg);
}
// 超时获取,并抛出中断异常
public final boolean tryAcquireSharedNanos(int arg, long nanosTimeout)
throws InterruptedException {
if (Thread.interrupted())
throw new InterruptedException();
return tryAcquireShared(arg) >= 0 ||
doAcquireSharedNanos(arg, nanosTimeout);
}
tryAcquireShared留给工具自己去实现,用于判断是否满足获取资源要求,与独占模式判断函数不一样,tryAcquire返回的是boolean,因为是独占模式,每次只能一个线程获取资源,所以直接返回boolean,共享模式下资源可以被多个线程通知占用,tryAcquireShared返回int类型,表示还有多少个资源可以同时被占用,用于共享模式下传播唤醒。
doAcquireShared
重点分析下doAcquireShared方法,doAcquireSharedInterruptibly和doAcquireSharedNanos区别不是很大,不做分析
private void doAcquireShared(int arg) {
//添加共享模式节点,主要区分独占模式
final Node node = addWaiter(Node.SHARED);
boolean failed = true;
try {
boolean interrupted = false;
for (;;) {
//拿到当前节点的前驱节点,如果前驱节点是head节点,说明没有等待节点
final Node p = node.predecessor();
if (p == head) {
// 尝试获取资源,大于等于0,说明有资源获取。
int r = tryAcquireShared(arg);
if (r >= 0) {
//把当前节点设置成head节点,并传播唤醒后面的节点。
setHeadAndPropagate(node, r);
p.next = null; // help GC
if (interrupted)
selfInterrupt();
failed = false;
return;
}
}
// 这里和独占模式一样,如果没资源申请,封装节点,并park等待
if (shouldParkAfterFailedAcquire(p, node) &&
parkAndCheckInterrupt())
interrupted = true;
}
} finally {
if (failed)
cancelAcquire(node);
}
}
setHeadAndPropagate
private void setHeadAndPropagate(Node node, int propagate) {
// 设置head节点,保留之前的head的节点,用于是否传播唤醒之后节点判断
Node h = head; // Record old head for check below
setHead(node);
/**
* 需要传播唤醒的几个条件
* 1,propagate>0,当大于0的时候,说明还有其他资源空余,需要传播唤醒之后的节点
* 2,head == null || head.waitStatus < 0,头结点为空,
head.waitStatus < 0即需要唤醒状态或者是传播状态,也无条件尝试唤醒之后的节点
* 唤醒之后的节点,会去重新尝试获取资源。
* 这里存在误唤醒,不过没关系,唤醒之后的节点,会继续回到doAcquireShared for循环中,尝试获取资源。
*/
if (propagate > 0 || h == null || h.waitStatus < 0 ||
(h = head) == null || h.waitStatus < 0) {
Node s = node.next;
// 判断是否是共享节点
if (s == null || s.isShared())
doReleaseShared();
}
}
以上是共享模式获取资源流程,释放资源函数如下:
doReleaseShared
private void doReleaseShared() {
for (;;) {
Node h = head;
if (h != null && h != tail) {
int ws = h.waitStatus;
// 如果head节点的后继节点需要被唤醒,然后唤醒
if (ws == Node.SIGNAL) {
if (!compareAndSetWaitStatus(h, Node.SIGNAL, 0))
continue; // loop to recheck cases
unparkSuccessor(h);
}
// 如果head节点后没有需要被唤醒的节点,设置成PROPAGATE状态,然后传播唤醒
else if (ws == 0 &&
!compareAndSetWaitStatus(h, 0, Node.PROPAGATE))
continue; // loop on failed CAS
}
if (h == head) // loop if head changed
break;
}
}