该方法目的是唤醒等待队列的第一个节点(即参数node指向的下一个节点)。看码源注释,为何要从尾节点向前寻找?因为会出现null或者waitStatus>0(即被cancel的情况)。
private void unparkSuccessor(Node node) {
int ws = node.waitStatus;
if (ws < 0)
compareAndSetWaitStatus(node, ws, 0);
Node s = node.next;
if (s == null || s.waitStatus > 0) {
s = null;
// 此处从tail向前寻找离head最近的下一个要被唤醒的节点
for (Node t = tail; t != null && t != node; t = t.prev)
if (t.waitStatus <= 0)
s = t;
}
if (s != null)
LockSupport.unpark(s.thread);
}
那为什么会出现next=null的情况?那需要看一下enq入队源码。如下:
private Node enq(final Node node) {
for (;;) {
Node t = tail;
if (t == null) { // Must initialize
if (compareAndSetHead(new Node()))
tail = head;
} else {
node.prev = t;
// 此处cas会有失败情况,无法执行t.next = node
if (compareAndSetTail(t, node)) {
t.next = node;
return t;
}
}
}
}
cas的失败导致t.next被设置失败,因此next会出现null的情况。