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.
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.await
darse 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
estructura familiar, como ReentrantLock y Semaphore, es una clase de sincronizador interno Sync
que hereda AQS, pero la diferencia es que CountDownLatch Sync
ya no es una clase abstracta.
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:
Semaphore
la cantidad de subprocesos que pueden pasar en AQSReentrantLock
y la realización de la característica de reentrada en el medio, todo depende destate
las 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 Sync
no 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.countDown
el 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#tryReleaseShared
el método y llamar al doReleaseShared
método AQS después del éxito. La diferencia es Semaphore#tryReleaseShared
que la implementación es sumar 1 al conteo, mientras que CountDownLatch#tryReleaseShared
la 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 doReleaseShared
mé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.await
indicar 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#tryAcquireShared
mé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 acquireSharedInterruptibly
método AQS podemos obtener las siguientes conclusiones:
-
Cuando el estado de sincronización es igual a 0,
tryAcquireShared
devuelva 1, no ejecutedoAcquireSharedInterruptibly
, es decir, ejecute suficientes vecescountDownLatch#countDown
, no necesita ingresar a la cola de espera; -
Cuando el estado de sincronización no es igual a 0,
tryAcquireShared
devuelve -1 y ejecutadoAcquireSharedInterruptibly
, es decir, si no se ha ejecutado las suficientes vecescountDownLatch#countDown
, debe ingresar a la cola de espera.
En pocas palabras, CountDownLatch#await
cuando 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!