Explicar los miembros de la familia AQS: CountDownLatch

Preste atención a Wang Youzhi , un pescador de oro mutuo que comparte la tecnología Java de núcleo duro.
Bienvenido a unirse al grupo de personas de Java : personas de Java que son ricas juntas

Hoy hablaremos de CountDownLatch, otro miembro importante de la familia AQS. No hay muchas preguntas de entrevista sobre CountDownLatch. Además de preguntar "qué" y "cómo implementar", CountDownLatch también se comparará con CyclicBarrier:

  • ¿Qué es CountDownLatch? ¿Cómo se logra?

  • ¿Cuál es la diferencia entre CountDownLatch y CyclicBarrier?

Como de costumbre, seguimos analizando CountDownLatch según los tres pasos de "qué", "cómo usar" y "cómo implementar". En cuanto a la diferencia con CyclicBarrier, la analizaremos en detalle en el próximo artículo.

Consejos : el "qué" y el "cómo usar" de hoy se fusionaron.

El uso de CountDownLatch

No sé si alguna vez ha participado en el tipo de trabajo en equipo de la empresa que conmovió al jefe y acompañó el tema de "mejorar" la cohesión organizacional. Por lo general, la administración organizará una actividad de senderismo a campo traviesa, estipulando que todos solo pueden comer después de llegar a la meta, lo que se denomina "el espíritu de equipo de no abandonar y no rendirse". Y el jefe esperará en la línea de meta temprano con una lista. Cuando el empleado llegue a la línea de meta, tachará su nombre en la lista. Rueda de tortura.

inserte la descripción de la imagen aquí

Entonces, una actividad de senderismo a campo traviesa de este tipo puede usar CountDownLatch para una descripción de código simple:

CountDownLatch countDownLatch = new CountDownLatch(10);

// 10个人进行越野徒步
for (int i = 0; i < 10; i++) {
  int finalI = i;
  new Thread(() -> {
    try {
      // 每个人比前一个选手晚1秒
      TimeUnit.SECONDS.sleep((finalI + 1));
    } catch (InterruptedException e) {
      throw new RuntimeException(e);
    }
    System.out.println("选手[" + finalI + "]到达终点!!!");
    countDownLatch.countDown();
  }).start();
}

// 老板在目的地吃瓜,等待每个选手到达
countDownLatch.await();
// 开饭啦!
System.out.println("老板说:所有人都到齐了,午饭是每人一个吐司!!!");

Al ver esto, ¿los amigos que han participado en tales actividades de creación de grupos tienen presión arterial alta? Pero no sea demasiado alto, porque en un edificio de equipo con presión arterial altísima, sin saberlo, hemos dominado el uso de CountDownLatch.

Primero tratemos de entender CountDownLatch por el nombre. CountDownLatch es una palabra compuesta. CountDown se traduce como "cuenta regresiva", y Latch se traduce como "pestillo de la puerta". La combinación significa abrir el pestillo después de la cuenta regresiva (para acciones posteriores). Veamos cómo explica Doug Lea el papel de CountDownLatch:

Una ayuda de sincronización que permite que uno o más subprocesos esperen hasta que se complete un conjunto de operaciones que se están realizando en otros subprocesos.

CountDownLatch es una ayuda de sincronización que permite que uno o más subprocesos esperen a que otros subprocesos completen operaciones (y, por lo tanto, realicen operaciones posteriores).

Cabe señalar que CountDownLatch permite que uno o más subprocesos esperen, y solo necesitamos llamarlo en diferentes subprocesos para CountDownLatch.awaitdarse cuenta de la espera de múltiples subprocesos.

El principio de CountDownLatch

Veamos primero cómo se relaciona CountDownLatch con AQS como miembro de la familia AQS: la
inserte la descripción de la imagen aquí
estructura familiar, como ReentrantLock y Semaphore, es una clase de sincronizador interno Syncque hereda AQS, pero la diferencia es que CountDownLatch Syncya no es una clase abstracta.
inserte la descripción de la imagen aquí
Dado que se hereda de AQS y hay contadores internos (la cuenta regresiva también es una cuenta), una vez más eliminamos la descripción del estado sincrónico como una característica del contador en " La vida actual de AQS, construyendo la base de JUC ". :

En AQS, el estado no solo se usa para representar el estado de sincronización, sino también un contador implementado por algunos sincronizadores , como: Semaphorela cantidad de subprocesos que pueden pasar en AQS ReentrantLocky la realización de la característica de reentrada en el medio, todo depende de statelas características del contador.

Aunque no hay un ejemplo de CountDownLatch, sé que después del análisis de Semaphore, debe poder adivinar cómo CountDownLatch usa el estado de sincronización como una función de contador. A continuación, echemos un vistazo a la aplicación del estado de sincronización en CountDownLatch.

Método de construcción

Como se puede ver en el diagrama de clases de los miembros de la familia AQS, el sincronizador en CountDownLatch Syncno distingue entre equidad e injusticia, por lo que el constructor solo necesita brindar la capacidad de establecer el conteo:

public class CountDownLatch {
  public CountDownLatch(int count) {
    if (count < 0){
      throw new IllegalArgumentException("count < 0");
    }
    this.sync = new Sync(count);
  }
  
  private static final class Sync extends AbstractQueuedSynchronizer {
    Sync(int count) {
      setState(count);
    }
  }
}

public abstract class AbstractQueuedSynchronizer extends AbstractOwnableSynchronizer implements java.io.Serializable {
  protected final void setState(int newState) {
    state = newState;
  }
}

Como era de esperar, el conteo de CountDownLatch aún se devuelve a AQS state.

método de cuenta regresiva

Volviendo a las actividades de senderismo, después de que los empleados lleguen a la meta, deben tachar sus nombres en la lista, y cuando llegue el último, sonarán los gongs y los tambores. En la implementación del código, usamos CountDownLatch.countDownel estado que indica la llegada del empleado y realizamos la acción correspondiente:

public class CountDownLatch {
  public void countDown() {
    sync.releaseShared(1);
  }
  
  private static final class Sync extends AbstractQueuedSynchronizer {
    protected boolean tryReleaseShared(int releases) {
      for (;;) {
        // 获取同步状态
        int c = getState();
        // 同步状态为0,返回失败
        if (c == 0){
          return false;
        }
        // 计数减1,并通过CAS更新
        int nextc = c - 1;
        if (compareAndSetState(c, nextc)) {
          // 计数器为0时返回true
          return nextc == 0;
        }
      }
    }
  }
}

public abstract class AbstractQueuedSynchronizer extends AbstractOwnableSynchronizer implements java.io.Serializable {
  public final boolean releaseShared(int arg) {
    if (tryReleaseShared(arg)) {
      doReleaseShared();
      return true;
    }
    return false;
  }
}

Recordando la implementación del método en " Explicación detallada de los miembros de la familia AQS: semáforoSemaphore#release " , ¿se siente familiarizado? Lo mismo es ejecutar Sync#tryReleaseSharedel método y llamar al doReleaseSharedmétodo AQS después del éxito. La diferencia es Semaphore#tryReleaseSharedque la implementación es sumar 1 al conteo, mientras que CountDownLatch#tryReleaseSharedla implementación es restar 1 del conteo.

Prestamos atención a otro problema, el método CountDownLatch devolverá verdaderoSync#tryReleaseShared solo cuando el contador se reduzca a 0. En este momento, puede ingresar al método AQS, de lo contrario, solo ejecuta la operación de disminuir el contador en uno.doReleaseShared

Además, también sabemos que el doReleaseSharedmétodo AQS juega un papel en la activación de los nodos en la cola de espera de AQS, es decir, solo cuando el contador se reduce a 0, CountDownLatch realizará un trabajo de activación .

Consejos : AQS ha sido analizado en doReleaseShared" Explicación detallada de los miembros de la familia AQS: semáforo ", así que no lo repetiré~~

método de espera

Sabemos que el jefe llegó al destino en automóvil temprano en la mañana y esperó, entonces, ¿cómo juzga el jefe que tiene que esperar? Después de que el jefe llegara a la meta antes de lo previsto, sacó la lista para contar el número de llegadas, y cuando vio que todavía había gente que no había llegado a la meta, estaba listo para tomar una siesta y dormir.

Solíamos CountDownLatch.awaitindicar que el jefe entró en estado de espera:

public class CountDownLatch {
  public void await() throws InterruptedException {
    sync.acquireSharedInterruptibly(1);
  }
}

public abstract class AbstractQueuedSynchronizer extends AbstractOwnableSynchronizer implements java.io.Serializable {
  public final void acquireSharedInterruptibly(int arg) throws InterruptedException {
    if (Thread.interrupted()) {
      throw new InterruptedException();
    }
    if (tryAcquireShared(arg) < 0) {
      doAcquireSharedInterruptibly(arg);
    }
  }
}

¿Todavía es familiar? El método AQS se usa como Semaphore acquireSharedInterruptibly, por lo que nos enfocamos en el Sync#tryAcquireSharedmétodo CountDownLatch:

public class CountDownLatch {
  private static final class Sync extends AbstractQueuedSynchronizer {
    protected int tryAcquireShared(int acquires) {
      // 同步状态为0返回1,不为0返回-1
      return (getState() == 0) ? 1 : -1;
    }
  }
}

Este método hace un juicio sobre el estado de sincronización, combinado con el acquireSharedInterruptiblymétodo AQS podemos obtener las siguientes conclusiones:

  • Cuando el estado de sincronización es igual a 0, tryAcquireShareddevuelva 1, no ejecute doAcquireSharedInterruptibly, es decir, ejecute suficientes veces countDownLatch#countDown, no necesita ingresar a la cola de espera;

  • Cuando el estado de sincronización no es igual a 0, tryAcquireShareddevuelve -1 y ejecuta doAcquireSharedInterruptibly, es decir, si no se ha ejecutado las suficientes veces countDownLatch#countDown, debe ingresar a la cola de espera.

En pocas palabras, CountDownLatch#awaitcuando se llama al método , el contador no es 0 para generar una cola de espera, y si es 0, no se ejecutará nada.

Consejos : AQS ha sido analizado en doAcquireSharedInterruptibly" Explicación detallada de los miembros de la familia AQS: semáforo ", así que no lo repetiré~~

epílogo

El contenido sobre CountDownLatch está aquí, y no hay mucho contenido. Cuando no estemos familiarizados con AQS o CountDownLatch, pensaremos que CountDownLatch es una herramienta "muy avanzada", pero cuando profundizamos en ella, encontraremos que la tecnología "avanzada" en realidad no es difícil de aprender.

Bueno, si este artículo es útil para usted, por favor felicítelo y apóyelo . Finalmente, todos pueden prestar atención a Wang Youzhi, un pescador , y la columna "¿ Qué preguntas en las entrevistas de Java?" ", ¡Hasta la próxima!

Supongo que te gusta

Origin blog.csdn.net/wyz_1945/article/details/131177426
Recomendado
Clasificación