De CountDownLatch a AQS

Escribir al frente

CountDownLatch código fuente para aprender AQS

manifestación

public static void main(String[] args) throws InterruptedException {
    
    
    CountDownLatch countDownLatch = new CountDownLatch(2);
    new Thread(()->{
    
    
       try {
    
    
           System.out.println("线程1执行");
           Thread.sleep(5000L);
       } catch (InterruptedException e) {
    
    
           e.printStackTrace();
       } finally {
    
    
           countDownLatch.countDown();
       }

    }).start();

     new Thread(()->{
    
    
        try {
    
    
            System.out.println("线程2执行");
            Thread.sleep(2000L);
        } catch (InterruptedException e) {
    
    
            e.printStackTrace();
        } finally {
    
    
            countDownLatch.countDown();
        }

     }).start();
     countDownLatch.await();
     System.out.println("两个线程都执行完执行此段代码");
}
  • El hilo principal, hilo 1, hilo 2 inicia la ejecución paralela
  • El hilo principal encuentra countDownLatch.await () esperando
  • Después de 2 segundos, se completa la ejecución del hilo 1 y se ejecuta countDownLatch.countDown ()
  • Después de 5 segundos, se completa la ejecución del hilo 2 y se ejecuta countDownLatch.countDown ()
  • Después de 2 conteos descendentes, el hilo principal espera que los bloques pasen
  • El número de veces depende del valor del método de construcción.

Análisis de código fuente

Método de construcción

Establezca el valor de estado en 2, sin otra lógica

countDownLatch.await ()

Llamada final

public final void acquireSharedInterruptibly(int arg)
       throws InterruptedException {
    
    
    if (Thread.interrupted())
        throw new InterruptedException();
     //尝试获取锁
    if (tryAcquireShared(arg) < 0)
        doAcquireSharedInterruptibly(arg);
}

tryAcquireShared

protected int tryAcquireShared(int acquires) {
    
    
    return (getState() == 0) ? 1 : -1;
}
  • Obtenga el valor del estado, si es igual a 0, devuelva 1; si no es igual a 0, devuelva -1.
  • Sabemos que si el estado es igual a 0, podemos continuar bajando. Según la demostración como ejemplo, el valor del estado inicial es 2, no igual a 0 (el hilo 1 y el hilo 2 no ejecutaron countDown), y devolverá -1

Continuar haciendoAdquirirCompartidoInterrumpidamente

private void doAcquireSharedInterruptibly(int arg)
    throws InterruptedException {
    
    
    final Node node = addWaiter(Node.SHARED);
    boolean failed = true;
    try {
    
    
        for (;;) {
    
    
            final Node p = node.predecessor();
            if (p == head) {
    
    
                int r = tryAcquireShared(arg);
                if (r >= 0) {
    
    
                    setHeadAndPropagate(node, r);
                    p.next = null; // help GC
                    failed = false;
                    return;
                }
            }
            if (shouldParkAfterFailedAcquire(p, node) &&
                parkAndCheckInterrupt())
                throw new InterruptedException();
        }
    } finally {
    
    
        if (failed)
            cancelAcquire(node);
    }
}

¿Hay una sensación de deja vu? Este es el método en aqs. La lógica de este método se ha discutido antes. Es decir, el subproceso principal se agrega a la cola clh, se establece en un estado compartido y luego continúa determinando si es el siguiente nodo del cabezal e intenta obtener un bloqueo compartido. Vuelva con éxito. Error, bloqueando el hilo actual

  • tryAcquireShared es diferente al anterior. Es una implementación de subclase de countdownlatch. Solo necesita determinar si el estado es 0. Solo cuando el estado es 0, el hilo seguirá bajando.
  • Recordamos que ahora la cola de subprocesos principal está bloqueada aquí
  • Cabecera del miembro de la cola -> hilo principal Aquí solo se bloquea un hilo principal
protected int tryAcquireShared(int acquires) {
    
    
     return (getState() == 0) ? 1 : -1;
 }

countDownLatch.countDown ()

La llamada final, o el método de aqs

public final boolean releaseShared(int arg) {
    
    
     //尝试释放锁
     if (tryReleaseShared(arg)) {
    
    
         //释放成功后唤醒线程
         doReleaseShared();
         return true;
     }
     return false;
 }

Implementación TryReleaseShared de la subclase CountDownLatch

protected boolean tryReleaseShared(int releases) {
    
    
    for (;;) {
    
    
        int c = getState();
        if (c == 0)
            return false;
        int nextc = c-1;
        if (compareAndSetState(c, nextc))
            return nextc == 0;
    }
}
  • Infinite loop plus cas es para tener en cuenta el problema de subprocesos múltiples
  • Obtenga el estado primero, de acuerdo con el ejemplo de la demostración, el estado es 2
  • Si el estado es igual a 0, el bloqueo se ha liberado y no es necesario volver a liberarlo.
  • Use cas para modificar 2 a 1, si la ejecución de múltiples subprocesos, el subproceso fallido ejecuta el siguiente bucle, establezca 1 en 0
  • Finalmente, el hilo cuyo estado se establece en 0 devuelve verdadero

doReleaseShared

O el método aqs, para activar el bloqueo compartido, consulte el análisis del código fuente de Semaphore. Después de despertarse, el hilo principal se bloqueará y se soltará. Después de que se ejecute el siguiente bucle, el bloqueo se adquiere, porque el estado es ya 0, y luego se ejecuta el código comercial.

Inserte la descripción de la imagen aquí
Lo anterior es el análisis general del código fuente de CountDownLatch. Si hay algún error, las críticas y correcciones son bienvenidas.

Supongo que te gusta

Origin blog.csdn.net/qq_37904966/article/details/113481253
Recomendado
Clasificación