AQS-анализ параллельного программирования

интервью

Структура диаграммы классов AQS

Полное имя AQS — AbstractQueuedSynchronizer, которое представляет собой абстрактную очередь синхронизации. Давайте взглянем на структуру диаграммы классов AQS:

image.png

государственная собственность

В AQS поддерживается изменчивое измененное состояние единого общего состояния для достижения синхронизации синхронизатора. В его обновлении используется алгоритм оптимистичной блокировки CAS.

очередь CLH

Синхронная очередь CLH (замки Крейга, Лэндина и Хагерстена) представляет собой двустороннюю очередь FIFO, которая записывает элементы головы и хвоста очереди через узлы головы и хвоста, а тип элементов очереди — Node. AQS полагается на него для завершения управления состоянием синхронизации.Если текущему потоку не удается получить состояние синхронизации, AQS создаст узел (узел) с такой информацией, как состояние ожидания текущего потока, и добавит его в очередь синхронизации CLH. , пока блокируется. Текущий поток, когда состояние синхронизации освобождается, разбудит первый узел (справедливая блокировка), чтобы он снова попытался получить состояние синхронизации.

image.png

Узел узел

В очереди синхронизации CLH узел представляет поток, который сохраняет ссылку на поток (поток), состояние (waitStatus), предшествующий узел (предыдущий), последующий узел (следующий) и последующий узел (nextWaiter) очереди условий. следующее:

image.png

waitStatus несколько состояний состояния:

image.png

зачислен

Запись в очередь CLH означает, что хвост указывает на новый узел, предыдущий узел нового узла указывает на текущий последний узел, а следующий из текущего последнего узла указывает на текущий узел. Метод addWaiter выглядит следующим образом:

//构造Node
private Node addWaiter(Node mode) {
    Node node = new Node(Thread.currentThread(), mode);
    // Try the fast path of enq; backup to full enq on failure
    Node pred = tail;
    if (pred != null) {
        node.prev = pred;
        //CAS设置尾节点
        if (compareAndSetTail(pred, node)) {
            pred.next = node;
            return node;
        }
    }
    //多次尝试
    enq(node);
    return node;
}

Если addWaiter не может установить хвостовой узел, вызовите метод enq(Node node), чтобы установить хвостовой узел.Метод enq выглядит следующим образом:

private Node enq(final Node node) {
    //死循环
    for (;;) {
        Node t = tail;
        //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;
            }
        }
    }
}

исключать из очереди

После того, как поток первого узла освободит состояние синхронизации, он разбудит свой узел-преемник (следующий), и узел-преемник установит себя в качестве первого узла, когда состояние синхронизации будет успешно получено. Вы можете увидеть следующий исходный код:

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);
}

Поток метода получения выглядит следующим образом:

image.png

Эксклюзивный и общий режим

AQS поддерживает два режима синхронизации: эксклюзивный и общий.

Эксклюзивный

Только один поток одновременно удерживает состояние синхронизации, например ReentrantLock. Его можно разделить на честный замок и несправедливый замок.

  • Справедливая блокировка: В соответствии с порядком очереди потоков в очереди вежливая блокировка в порядке поступления.
  • Несправедливые блокировки: когда поток хочет получить блокировку, он захватывает ее напрямую, независимо от порядка очереди.

общий

Несколько потоков могут выполняться одновременно, например, Semaphore/CountDownLatch и т. д. являются общими продуктами.

Ссылаться на

nuggets.capable/post/684490…