CountdownLatch es una clase de herramientas útil. Usándolo, podemos interceptar uno o más hilos para ejecutar después de que una determinada condición esté madura. Su contenido interno proporciona un contador, y el valor inicial del contador debe especificarse al construir un bloqueo, y el valor inicial del contador debe ser mayor que 0. Además, también proporciona un método de cuenta regresiva para operar el valor del contador. El contador se disminuirá por 1 cada vez que se llame al método de cuenta regresiva. Cuando el valor del contador se reduce a 0, significa que las condiciones están maduras, y todos los hilos bloqueados llamando al método de espera se despertarán. Este es el mecanismo interno de CountdownLatch. Se ve muy simple, no es más que bloquear algunos hilos y permitirles ejecutarse después de alcanzar una determinada condición. Sin embargo, CountdownLatch tiene una amplia gama de escenarios de aplicación. Mientras tengas una gran mente y la uses, puedes jugar varios trucos. El escenario de aplicación más común es habilitar múltiples hilos para ejecutar una tarea al mismo tiempo, y luego contar y resumir los resultados después de que se ejecuten todas las tareas. La siguiente figura demuestra dinámicamente todo el proceso de bloqueo de hilos.
La figura anterior muestra que 5 hilos están bloqueados llamando al método de espera, y deben esperar a que el valor del contador disminuya a 0 antes de continuar ejecutándose. El valor inicial del contador se especifica al construir un bloqueo, y posteriormente se reduce en 1 con cada llamada al método de cuenta regresiva. El siguiente código publica el método de construcción CountdownLatch.
// Constructor public CountdownLatch (int count) {if (count <0) tirar nueva ilegalargumentException ("count <0"); this.sync = new Sync (Count); }CountdownLatch tiene solo un constructor de parámetros, y se debe pasar un valor mayor que 0 como valor inicial del contador, de lo contrario se informará un error. Puede ver eso en el constructor, solo un objeto de sincronización nuevo y asignarlo a la sincronización de la variable miembro. Al igual que otras clases de herramientas de sincronización, la implementación de CountdownLatch se basa en AQS, que es una aplicación en modo compartido AQS. CountdownLatch implementa una sincronización de clase interna y la usa para heredar AQS, de modo que se puedan utilizar la mayoría de los métodos proporcionados por AQS. Echemos un vistazo al código de clase interna de sincronización.
// Synchronizer Private Static Final Class Sync extiende AbstractqueedSynChronizer {// Constructor Sync (int count) {setState (count); } // Obtenga el estado de sincronización actual int getCount () {return getState (); } // Intenta obtener el número de bloqueo // Número negativo de retorno: indica que el hilo actual no pudo obtener // Valor de retorno cero: indica que el subproceso actual se ha adquirido con éxito, pero el hilo posterior ya no puede obtener // retorno de número positivo: indica que el hilo actual se ha adquirido con éxito, y el subproceso posterior también puede obtener un éxito exitoso intacto (intesishars) {retorno (retorno (retorno (retraso GetState (). 1: -1; } // Intenta liberar el bloqueo booleano protegido de bloqueo booleano (int lotess) {for (;;) {// Obtener el estado de sincronización int c = getState (); // Si el estado de sincronización es 0, if (c == 0) {return false; } // de lo contrario, reduzca el estado de sincronización en 1 int nextc = c-1; // Use el método CAS para actualizar el estado de sincronización if (compareSEnSetState (c, nextc)) {return nextC == 0; }}}}Puede ver que el constructor de Sync establecerá el valor del estado de sincronización en el valor del parámetro aprobado. Después de eso, cada vez que se llama el método de cuenta regresiva, el valor del estado síncrono se reducirá en 1, que es el principio de implementación del mostrador. Los dos métodos más utilizados cuando se usan la clase de herramienta CountdownLatch son el método APARATIVO y el método Countdown. Llamar al método de espera bloqueará el hilo actual hasta que el contador sea 0, y llamar al método de cuenta regresiva disminuirá el valor del contador en 1 hasta que se reduzca a 0. Echemos un vistazo a cómo se llama al método de espera.
// Hacer que el hilo actual espere hasta que el pestillo disminuya a 0, o el hilo se interrumpe en público void await () lanza interruptedException {// adquirir sync.acquircarSarcaredInterruptable (1);} // bloquear el bloqueo en el modo interrumpible (modo compartido) público final void void adquirircaredInterruptable (int arg). (Thread.interrupted ()) {lanzar una nueva interrupciónxception (); } // 1. Intente adquirir el bloqueo if (tryAcquireshared (arg) <0) {// 2. Si la adquisición falla, ingrese el método doAcquiresharedInterruptiblemente (arg); }}Cuando el hilo llama al método de espera, en realidad llama al método de adquisición de AQS. Este método adquiere el bloqueo en respuesta a las interrupciones de hilo. El código para este método también se publica arriba. Podemos ver que en el método de adquisición de Interruption, primero, llamará al método TryAcQuireshared para intentar adquirir el bloqueo. Vemos la lógica del método Tryacquireshared reescrito en sincronización. La lógica de implementación del método es muy simple, lo que es juzgar si el estado de sincronización actual es 0. Si es 0, el retorno 1 significa que el bloqueo se puede adquirir, de lo contrario, devolver -1 significa que el bloqueo no se puede adquirir. Si el método TryAcquireshared devuelve 1, el hilo puede continuar ejecutándose sin esperar. Si se devuelve -1, entonces el método DoAcquireshedinterruptualy se llamará en la cola sincrónica para esperar. Este es el principio de que llamar al método de espera bloqueará el hilo actual. Veamos cómo el método de cuenta regresiva despierta el hilo de bloqueo.
// Método para reducir el LATCH public void Countdown () {Sync.Releasshared (1);} // Operación de lanzamiento (Modo compartido) Public Boolean Boolean Releasshared (int arg) {// 1. Intente liberar el bloqueo if (tryreleasshared (arg)) {// 2. Si el lanzamiento es exitoso, despierta otros hilos doreleasshared (); devolver verdadero; } return false;}Puede ver que el método desasasado se llama en el método Countdown. Este método también es un método en AQS. También publicamos su código en él. Lo primero en el método desagradable es llamar al método de Tryreleasashared para intentar liberar el bloqueo. El método de transferencia de tryreleasas es un método abstracto en AQS. Su lógica de implementación específica se encuentra en la clase de sincronización de subclase. Podemos encontrar este método en el código de clase Sync publicado anteriormente. Si el método Tryreleaseured devuelve verdadero a la versión y devuelve falso a la falla de liberación. Solo devolverá verdadero si el estado de sincronización es exactamente 0 después de disminuir 1. En otros casos, se devolverá falso. Luego, cuando Tryreleaseured devuelve verdadero, se llamará al método dorelebrador de atención inmediata inmediatamente para despertar todos los hilos en la cola de sincronización. Esto explica por qué la última vez que se llama al método de cuenta regresiva para reducir el contador a 0 despertará todos los hilos bloqueados. Estos son los principios básicos de CountdownLatch. Echemos un vistazo a un ejemplo de su uso.
Escenario de aplicación: al jugar al propietario feliz, debes esperar a que lleguen los tres jugadores antes de que puedas tratar cartas.
Public Class Player extiende Thread {private static int count = 1; Private final int id = count ++; pestillo privado CountdownLatch; Public Player (CountdownLatch Latch) {this.latch = Latch; } @Override public void run () {System.out.println ("【Player" + id + "] Entrada"); Latch.CountDown (); } public static void main (string [] args) lanza interruptedException {CountdownLatch Latch = new CountdownLatch (3); System.out.println ("El juego comienza, esperando que el jugador ingrese ..."); nuevo jugador (Latch) .Start (); nuevo jugador (Latch) .Start (); nuevo jugador (Latch) .Start (); Latch.Await (); System.out.println ("Los jugadores han llegado, comienzan a tratar ..."); }}Los resultados de la operación muestran que la operación de trato debe llevarse a cabo después de que todos los jugadores ingresen al campo. Comentamos los 23 líneas Latch.Await () y lo comparamos para ver los resultados.
Puede ver que después de comentar la línea Latch.Await (), no se garantiza que todos los jugadores comenzarán a tratar cartas solo después de ingresar al campo.
Lo anterior es todo el contenido de este artículo. Espero que sea útil para el aprendizaje de todos y espero que todos apoyen más a Wulin.com.