Когда я прочитал исходный код Java об AQS (AbstractQueuedSynchronizer), у меня возникло сомнение.
final boolean isOnSyncQueue(Node node) {
if (node.waitStatus == Node.CONDITION || node.prev == null)
return false;
if (node.next != null) // If has successor, it must be on queue
return true;
/*
* node.prev can be non-null, but not yet on queue because
* the CAS to place it on queue can fail. So we have to
* traverse from tail to make sure it actually made it. It
* will always be near the tail in calls to this method, and
* unless the CAS failed (which is unlikely), it will be
* there, so we hardly ever traverse much.
*/
return findNodeFromTail(node);
}
Основным сомнением является последний комментарий. Когда поток, ожидающий условия, сообщается другим потоком, вызывающим метод signal()
, его узел помещается в очередь синхронизации.
final boolean transferForSignal(Node node) {
/*
* If cannot change waitStatus, the node has been cancelled.
*/
if (!node.compareAndSetWaitStatus(Node.CONDITION, 0))
return false;
/*
* Splice onto queue and try to set waitStatus of predecessor to
* indicate that thread is (probably) waiting. If cancelled or
* attempt to set waitStatus fails, wake up to resync (in which
* case the waitStatus can be transiently and harmlessly wrong).
*/
Node p = enq(node);
int ws = p.waitStatus;
if (ws > 0 || !p.compareAndSetWaitStatus(ws, Node.SIGNAL))
LockSupport.unpark(node.thread);
return true;
}
Если другой поток не выполнит метод enq(Node)
полностью, этот узел будет пытаться последовательно compareAndSetTail(oldTail, node)
до успеха. После того, как он выполнит финиш, этот поток может получить сигнал.
Я могу представить себе обстоятельство, что при вызове метода enq
этот поток "неожиданно просыпается" и выполняет метод isOnSyncQueue
. На этот раз его waitStatus не CONDITION, а его prev установлен в 'enq', поэтому он может соответствовать тому, что сказано в комментарии. Но из-за сбоя CAS узла, когда он вызывает метод findNodeFromTail
, он не находится в очереди синхронизации. findNodeFromTail
не имеет смысла.
Когда enq
вызывает успешно, и предыдущий узел отменяется, он разархивирует этот поток, но поток неожиданно проснулся, метод isOnSyncQueue
вернет false, и поток снова заблокируется. В этот раз это не будет сигнал снова.
if (ws > 0 || !p.compareAndSetWaitStatus(ws, Node.SIGNAL))
LockSupport.unpark(node.thread);
ниже - то, что я говорю (мне не разрешено просматривать картинку):
thread1: transferForSignal() enq()
thread2: surprise wake up. execute isOnSyncQueue() and return fasle
thread1: enq() execute finish and prev is cancelled or CAS fail,
LockSupport.unpark(node.thread), this is make no sense.
thread2: LockSupport.park(this) and won't be signalled because
LockSupport.unpark(node.thread) has been invoked.
Итак, я прав? Или вы можете сказать мне, почему? Спасибо за ответ.