Componentes relacionados a AQS da condição de simultaneidade java

O que é condição

  • A condição é uma interface
  • Ele fornece um método análogo ao Objeto do monitor, com o Bloqueio pode ser implementado o modo de espera / notificação .
  • Comparado ao método do monitor do objeto
    • Pode suportar várias filas de espera
    • Suporte a interrupção enquanto espera
    • Tempo limite de espera de suporte
  • A criação do objeto Condition depende do objeto Lock
    • No código-fonte do Lock, podemos ver Condition newCondition();a definição de Get Condition
  • A realização da condição é realizada em AQS

Aqui, precisamos dizer que um objeto de bloqueio corresponde a várias condições.
O significado aqui é equivalente ao de todos os threads sob o mesmo bloqueio (recurso), existem diferentes relacionamentos condicionais entre eles, então precisamos definir as condições separadamente

Uso de condição e análise de código-fonte

Conforme mencionado na introdução anterior, a Condição é definida em Lock.
Então, quando precisamos usar a Condição, precisamos lock对象.newContion()obtê- la por meio de métodos.

Em seguida, você pode usar o método de condição object.await () para inserir a espera e liberar o bloqueio ao mesmo tempo.
Em seguida, outros objetos ativam os threads em espera chamando o método object.signal () da condição e liberam o bloqueio ao mesmo tempo.

Realização da Condição

A condição é uma interface e sua implementação específica está em AQS, que existe como uma classe interna de AQS.
A lógica aqui é mais clara, porque Condition depende da existência de Lock, e Lock deve usar AQS, então o arranjo é assim. Deve haver AQS no objeto Lock e deve haver uma Condição.

No código-fonte AQS, podemos encontrar a classe de implementação ConditionObject of Condition:

public class ConditionObject implements Condition, java.io.Serializable 

A interface Condition não define uma fila, mas sua classe de implementação define uma fila de espera.

  • Esta é uma fila unilateral
  • Para mantê-lo, os nós firstWaiter e lastWaiter são usados
  • O nó aqui é um nó que reutiliza a fila de sincronização no AQS (para que cada nó tenha uma referência de encadeamento)
  • Cada objeto de condição tem uma fila de espera
  • Um objeto de bloqueio pode ter vários objetos de condição, portanto, um objeto de bloqueio tem uma fila de sincronização e várias filas de espera de condição
    Insira a descrição da imagem aqui

Vamos dar uma olhada no processo de Condição de espera, fila de espera e notificação:

esperar:

  • Em primeiro lugar, deve ficar claro que, como wait (), os threads que podem realizar espera e notificação são todos os threads que adquiriram o bloqueio (ou estado de sincronização), portanto, não há problema de sincronização aqui. O bloqueio nos ajuda resolver o problema de segurança do thread.
  • Quando um thread executa o método await (), ele libera o bloqueio e, em seguida, constrói um novo nó para se juntar ao final da fila de espera e entrar no estado de espera.
  • Em seguida, ele permanece na fila e permanece lá até que alguém sinalize, acorde a fila de espera e adiciona o primeiroWaiter (nó) da fila de espera ao final da fila de sincronização. Até eu entrar na fila de sincronização

Aviso prévio:

  • Como await (), a thread executando o método de sinal também adquire o bloqueio
  • Ele libera o bloqueio e, em seguida, espera que o firstWaiter da fila se mova para a fila síncrona e, em seguida, ativa o nó.

Vamos dar uma olhada no código-fonte: o
primeiro é await ():

	public final void await() throws InterruptedException {
    
    
            if (Thread.interrupted())
                throw new InterruptedException();
            //当前节点加入等待队列
            Node node = addConditionWaiter();
            //释放同步状态
            int savedState = fullyRelease(node);
            int interruptMode = 0;
            
            while (!isOnSyncQueue(node)) {
    
    
                LockSupport.park(this);
                if ((interruptMode = checkInterruptWhileWaiting(node)) != 0)
                    break;
            }
            if (acquireQueued(node, savedState) && interruptMode != THROW_IE)
                interruptMode = REINTERRUPT;
            if (node.nextWaiter != null) // clean up if cancelled
                unlinkCancelledWaiters();
            if (interruptMode != 0)
                reportInterruptAfterWait(interruptMode);
        }

Veja meus comentários no código

	private Node addConditionWaiter() {
    
    
            Node t = lastWaiter;
            // If lastWaiter is cancelled, clean out.
            if (t != null && t.waitStatus != Node.CONDITION) {
    
    
                unlinkCancelledWaiters();
                t = lastWaiter;
            }

            Node node = new Node(Node.CONDITION);

            if (t == null)
                firstWaiter = node;
            else
                t.nextWaiter = node;
            lastWaiter = node;
            return node;
        }

Aqui é criar um nó, o modo do nó é Condição e, em seguida, inseri-lo no final da fila de espera

	final int fullyRelease(Node node) {
    
    
        try {
    
    
            int savedState = getState();
            if (release(savedState))
                return savedState;
            throw new IllegalMonitorStateException();
        } catch (Throwable t) {
    
    
            node.waitStatus = Node.CANCELLED;
            throw t;
        }
    }

	public final boolean release(int arg) {
    
    
        if (tryRelease(arg)) {
    
    
            Node h = head;
            if (h != null && h.waitStatus != 0)
                unparkSuccessor(h);
            return true;
        }
        return false;
    }

Você pode ver que o estado de sincronização é liberado aqui e você realmente precisa chamar o método tryRelease do AQS para liberar o estado de sincronização.

Em seguida, chame o método LockSupport para bloquear o thread.

Em seguida, observe o método de sinal:

	public final void signal() {
    
    
        if (!isHeldExclusively())
             throw new IllegalMonitorStateException();
         Node first = firstWaiter;
         if (first != null)
             doSignal(first);//这里
      }
	private void doSignal(Node first) {
    
    
         do {
    
    
             if ( (firstWaiter = first.nextWaiter) == null)
                 lastWaiter = null;
             first.nextWaiter = null;
         } while (!transferForSignal(first) &&//看这
                  (first = firstWaiter) != null);
      }
	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;
    }

Pode-se ver aqui que durante o sinal, o objeto desperto é o firstWaiter.

Após a chamada camada por camada, o nó é primeiro adicionado à fila de sincronização por meio da função enq e, em seguida,
o thread firstWaiter é ativado usando o método unpark de LockSupport.

Acho que você gosta

Origin blog.csdn.net/qq_34687559/article/details/114287156
Recomendado
Clasificación