当我阅读Java关于AQS(AbstractQueuedSynchronizer)的消息来源时,我有一个疑问。
private void unparkSuccessor(Node node) {
/*
* If status is negative (i.e., possibly needing signal) try
* to clear in anticipation of signalling. It is OK if this
* fails or if status is changed by waiting thread.
*/
int ws = node.waitStatus;
if (ws < 0)
compareAndSetWaitStatus(node, ws, 0);
/*
* Thread to unpark is held in successor, which is normally
* just the next node. But if cancelled or apparently null,
* traverse backwards from tail to find the actual
* non-cancelled successor.
*/
Node s = node.next;
if (s == null || s.waitStatus > 0) {
s = null;
for (Node t = tail; t != null && t != node; t = t.prev)
if (t.waitStatus <= 0)
s = t;
}
if (s != null)
LockSupport.unpark(s.thread);
}
主要疑虑是:
if (s == null || s.waitStatus > 0) {
s = null;
for (Node t = tail; t != null && t != node; t = t.prev)
if (t.waitStatus <= 0)
s = t;
}
为什么如果取消或显然为空,从尾部向后移动以找到实际未取消的继承人?而不是从头部找到实际未取消的继任者? 谢谢你的回答。
答案 0 :(得分:1)
这是一种保守的设计。在next
方法中分配enq
,设置新尾部并将先前尾部的next
引用链接到新尾部不是原子操作。在for循环中,它首先分配prev字段,如果赢得compareAndSetTail
,则在新尾节点旁边分配prev
。
因此next
路径是优化,如果next
不为空并且next
未取消,而不是向后遍历。
因此,查看null next字段并不一定意味着该节点位于队列末尾。 取消节点的下一个字段设置为指向节点本身而不是null,以使isOnSyncQueue的生活更轻松。
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;
if (compareAndSetTail(t, node)) {
t.next = node;
return t;
}
}
}
}