关于共享锁中doReleaseShared()理解
public final boolean releaseShared(int arg) {
//同一时刻只允许一个线程进入doReleaseShared()方法
if (tryReleaseShared(arg)) {
doReleaseShared();
return true;
}
return false;
}
理想情况下唤醒这个节点后面所有的线程。
private void doReleaseShared() {
for (;;) {
Node h = head;
if (h != null && h != tail) {
int ws = h.waitStatus;
//这里说明了头节点后面是有节点的,需要被唤醒。
if (ws == Node.SIGNAL) {
//这里更新失败,是因为会出现并发情况,因为刚唤醒的线程也会调用doReleaseShared()所以此时是有多个线程同时执行这个方法。
if (!compareAndSetWaitStatus(h, Node.SIGNAL, 0))
continue; // loop to recheck cases
unparkSuccessor(h);
}
//head.waitStatus=0的情况有两种
//1、就是head节点没有及时更新,线程被唤醒之后获取到了锁,在更新head之前,又经过一轮循环执行到这。 但是如果节点没有及时更新就会退出。所以执行到这一步只可能是情况2
//2、head节点及时更新了,但是到了最后一个节点,它的head.waitStatus=0
else if (ws == 0 &&
!compareAndSetWaitStatus(h, 0, Node.PROPAGATE))
continue; // loop on failed CAS
}
//节点没有及时更新,就退出。
if (h == head) // loop if head changed
break;
}
}
所以说整体的逻辑就是在head能及时更新的情况下,唤醒队列中所有的节点,直到队列中只有head。
并将这个head设置为compareAndSetWaitStatus(h, 0, Node.PROPAGATE)
然后通过下面代码退出。
if (h == head) // loop if head changed
break;
那什么时候compareAndSetWaitStatus(h, 0, Node.PROPAGATE)修改会失败呢?
就是当ws==0判断后面的一瞬间又有新的节点加入然后将head的值又改为-1,然后执行continue又会将刚加入的节点给唤醒。
else if (ws == 0 &&!compareAndSetWaitStatus(h, 0, Node.PROPAGATE))
continue;
所以以上代码是尽可能的将所有的节点给唤醒,而唤醒的线程又会帮助去唤醒线程,包括新加入的节点。