共计 1486 个字符,预计需要花费 4 分钟才能阅读完成。
概述
跟之前的思路一样咱们也采纳自顶向下的办法来剖析 release 的具体实现
独占模式:release 办法
这个办法就是独占模式下的开释锁的入口办法,能够看到十分的简略就是在开释锁胜利当前,要唤醒 head 节点的无效后继节点来竞争锁,这里有个细节就是 waitStatus != 0,我猜想是为了避免多个线程同时调用 release 办法,且都 tryRelease 胜利了,避免屡次同时唤醒 head 节点的后继节点,从而导致后继节点的下次 park 间接返回?因为在 unparkSuccessor 中会把 head 节点的 waitStatus 赋值为 0,然而我感觉仿佛这段代码也不是线程平安的,如果线程 A 与线程 B 同时进入,它们同时看到的 head 节点的 waitStatus 为 SIGNAL,而后它们就会同时进入 unparkSuccessor 办法,那么此处的 waitStatus != 0 意义何在呢?
public final boolean release(int arg) {if (tryRelease(arg)) {
Node h = head;
if (h != null && h.waitStatus != 0)
unparkSuccessor(h);
return true;
}
return false;
}
共享模式:releaseShared
共享模式下在开释共享锁胜利后就间接调用 doReleaseShared 办法来解决了,上面再具体分析下
public final boolean releaseShared(int arg) {if (tryReleaseShared(arg)) {doReleaseShared();
return true;
}
return false;
}
共享模式:doReleaseShared
共享模式下的开释锁办法,须要唤醒后继并确保信号流传,然而在独占模式下,只须要唤醒 head 节点的后继即可。
1.head 节点不为 null 且不等于 tail 节点的状况下
1. 判断 head 节点的 waitStatus 如果等于 SIGNAL,意味着要唤醒后继,多线程的状况下,只有一个线程能够胜利设置 waitStatus 为 0,并且唤醒后继节点,其余线程会再次进入循环,然而不会进入 SIGNAL 分支了
2. 判断 head 节点的 waitStatus 等于 0 且用 CAS 来更新 waitStatus 状态为 PROPAGATE 状态,只有一个线程能够胜利更新,其余线程会更新失败,会再次进入循环,但此时读取到的 head 节点的 waitStatus 肯定是 PROPAGATE 了,就不须要在循环了。
3. 判断以后线程持有的 head 没有被其余线程批改,如果批改了就从新执行上述流程,否则就退出循环,到了这一步,其实 head 的 waitStatus 有俩种,一种为 0,是在失常唤醒了后继节点的状况下,或者为 PROPAGATE,在没有失常唤醒后继节点的状况。
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; // 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;
}
}