共享模式
共享模式允许一组线程获取同一个许可。为实现共享模式子类需要实现两个方法:
- tryAcquireShared:返回int类型的值,小于0表示获取失败,等于0表示获取成功但不允许后续更多的获取,大于0表示获取成功且允许更多的后续获取。
- tryReleaseShared:返回true表示释放许可成功,可以唤醒等待线程;false表示失败,不唤醒等待线程。
共享获取 acquireShared
public final void acquireShared(int arg) {
if (tryAcquireShared(arg) < 0)
doAcquireShared(arg);
}
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) {
// 获取成功
// 把自己设为头结点并传递可以获取的信号
// node 把自己设为头结点后,它的后继发现它的前驱是头结点了,就会尝试获取。
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; // Record old head for check below
setHead(node);
/*
* 尝试通知队列里的下一个结点,如果:
* 调用者指示或者之前操作记录显示需要传递
* (注意:这里对waitStatus使用单一检查,因为PROPAGATE可能被转换到SIGNAL)
* 并且
* 下一个结点以共享模式等待或者我们根本就不知道,因为它是空的。
*
* 在这些检查有点保守,可能导致不必要的唤醒,但只是在多重竞争acquires/releases时,
* 因此,大多数都是现在或不久就需要通知的。
*/
if (propagate > 0 || h == null || h.waitStatus < 0) {
Node s = node.next;
if (s == null || s.isShared())
doReleaseShared();
}
}
private void setHead(Node node) {
head = node;
node.thread = null; // for GC
node.prev = null;
}