Как понять функцию «isOnSyncQueue» в AQS - PullRequest
1 голос
/ 12 марта 2019

Когда я прочитал исходный код 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.

Итак, я прав? Или вы можете сказать мне, почему? Спасибо за ответ.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...