Análisis del código fuente CountdownLatch - Countdown ()
El artículo anterior habló sobre el principio de Await () en CountdownLatch desde el nivel del código fuente. Este artículo habla sobre Countdown ().
public void CountDown () {// CountdownLatch Sync.ReleaseShared (1);} ↓ public Final Boolean Rehesseshared (int arg) {// aqs if (tryreleaseShared (arg)) {doreleaseShared (); devolver verdadero; } return false;} ↓ Protegido de tryrelebre booleano (int Lotes) {//countdownlatch.sync // recuento de decrementos; señal cuando la transición a cero para (;;) {int c = getState (); if (c == 0) return false; int nextc = c-1; if (compareSetState (c, nextC)) return nextc == 0; }}A través del constructor CountDownLatch final = new CountdownLatch (2); el estado se establece en 2, por lo que c == 2, nextc = 2-1,
Luego establezca el estado en 1 a través de la siguiente operación CAS.
Comparación booleana final protegida (int espere, int actualización) {// Consulte a continuación la configuración intrínseca para admitir este retorno insefe.compareandswapint (this, stateOffset, esperanza, actualización); }En este momento, NextC no es 0 y devuelve falso. Espere hasta que el método Countdown () se llame dos veces, State == 0, NextC == 0 y devuelva verdadero en este momento.
Ingrese el método doreleasshared ().
doreleasshared (); ↓ vacío privado doreleasshared () { / * * Asegúrese de que una liberación se propage, incluso si hay otras * adquirir /versiones en progreso. Esto procede en la forma habitual * de tratar de impartar a la cabeza si necesita * señal. Pero si no es así, el estado se propagará para * asegurarse de que al liberar, la propagación continúa. * Además, debemos recorrer en caso de que se agregue un nuevo nodo * mientras lo hacemos. Además, a diferencia de otros usos de * UnbarkSuccessor, necesitamos saber si CAS para restablecer el estado * falla, si es así, se vuelve a ver. */ for (;;) {nodo h = head; if (h! = null && h! = tail) {int ws = h.waitstatus; if (ws == node.signal) {if (! compareandsetwaitstatus (h, node.signal, 0)) continúa; // bucle para volver a verificar los casos UnkSuccessor (H); } else if (ws == 0 &&! compareandsetwaitstatus (h, 0, node.propagate)) continuar; // bucle en el fallido CAS} if (h == head) // bucle si la cabeza cambió el descanso; }}Recordando el modelo de cola de espera en este momento.
+----------------------------------------------------------------------------------------------- ---------------------------------------------------------------------------------------------------
En este momento, la cabeza no es nula ni cola. waitStatus == node.signal, así que ingrese el juicio if (! compareandsetwaitstatus (h, node.signal, 0)).
if (! compareandsetwaitstatus (h, node.signal, 0)) ↓ /*** CAS WailStatus de un nodo. */Private static final Boolean compareandSetWaitStatus (nodo de nodo, int espere, int actualización) {return unsafe.compareandswapint (nodo, waitStatusoffset, esperanza, actualización);}Esta operación CAS establece el estado en 0, lo que significa que Wailstatus en la cabeza es 0 en este momento. El modelo de cola es el siguiente
+----------------------------------------------------------------------------------------------- ---------------------------------------------------------------------------------------------------
Este método devuelve verdadero. Ingrese a UnparkSuccessor (H);
UnbarkSuccessor (H); ↓ Void privado UnbarkSuccessor (nodo de nodo) { / * * Si el estado es negativo (es decir, posible señal de necesidad) intente * borrar en anticipación de la señalización. Está bien si esto * falla o si el estado se cambia al esperar el hilo. */ int ws = node.waitStatus; if (ws <0) compareandsetwaitstatus (nodo, ws, 0); / * * El hilo para nok se mantiene en sucesor, que normalmente es * solo el siguiente nodo. Pero si se cancela o aparentemente nulo, * atraviesa hacia atrás desde la cola para encontrar el sucesor real * no cancelado. */ Nodo s = node.next; if (s == null || s.waitstatus> 0) {s = null; for (nodo t = cola; t! = null && t! = nodo; t = t.prev) if (t.waitStatus <= 0) s = t; } if (S! = NULL) LOCKSUPPORT.UNPARK (S.Thread);}S es el nodo sucesor de la cabeza, es decir, el nodo con el hilo actual. S! = NULL, y S.WAITSTATUS == 0, así que ingrese a Locksupport.unpark (S.Thread);
public static void Unpark (hilo de hilo) {if (hilo! = null) unsafe.unpark (hilo); }Es decir, el hilo que desbloquea bloqueado. ¡Al árbitro se le permitió soplar el silbato!
El principio de Countdown () es muy claro.
Cada vez que se ejecuta el método Countdown (), el estado se reduce en 1. Hasta el estado == 0, los subprocesos bloqueados en la cola comienzan a liberarse, y los hilos en los nodos posteriores se liberan de acuerdo con el estado de Wailstatus en el nodo predecesor.
Ok, vuelva a la pregunta del artículo anterior, ¿cuándo se romperá el siguiente bucle (el bucle en el método de espera)?
para (;;) {nodo final p = node.pedecesor (); if (p == head) {int r = tryacquireshared (arg); if (r> = 0) {setheadandpropagate (nodo, r); p.next = nulo; // Ayuda GC falló = falso; devolver; }} if (deber (deberíaParkAfterFailedAcquire (P, nodo) && ParkandCheckInterrupt ()) Lanzar nueva interrupciónxception ();}En este momento, estado == 0, así que ingrese el método SetheadAndPropagate.
setheadandpropagate (nodo, r); ↓ vacío privado setheadandpropagate (nodo de nodo, int propagate) {nodo h = head; // registrar la cabeza antigua para verificar por debajo de Sethead (nodo); / * * Intenta señalar el siguiente nodo en cola si: * La propagación fue indicada por la persona que llamó, * o fue registrado (como h.waitStatus, ya sea antes * o después de Sethead) mediante una operación anterior * (nota: Esto usa el signo de la verificación de WailStatus porque * Propagate State puede transición a la señal.) * Y * el siguiente nodo está esperando en modo compartido, * o no sabemos, porque parece null * la propagación de la transición a la señal de la señal de la señal. despertares innecesarios, pero solo cuando hay múltiples * ganancias de carreras, por lo que la mayoría necesita señales ahora o pronto * de todos modos. */ if (propagate> 0 || h == null || h.waitstatus <0 || (h = head) == null || h.waitstatus <0) {nodo s = node.next; if (s == null || s.isshared ()) doreleasshared (); }} ↓ vacío privado sethead (nodo de nodo) {head = node; node.thread = null; node.prev = null;}Este método cambia el nodo sucesor de la cabeza. Después de este método, el siguiente nodo del nodo se establece en NULL, y el modelo se convierte en la siguiente figura
previsamente +------------------------------------------------------------------------------------------- ---------------------------------------------------------------------------------------------------
Es decir, la cola de cabeza de nodo y otras cosas están configuradas para nular, esperando que GC recicle. En este momento, regrese, salte del bucle for, y la cola se borra.
Aquí hay una demostración de todo el proceso.
setheadandpropagate (nodo, r); +----------------------------------------------------------------------------------------------- ----------------------------------------------------------------------------------------------- Thread = NULL | <---- nodo (cola) | CurrentThread | +--------------------------------------------------------------- ------------------------------------------------------------------------------------------- nodo (cola) | CurrentThread | + -------------------------------+ ↓ +------------------------------------------------------------------------------------------- -------------------------------------------------------------------------------------------
El núcleo de CountdownLatch es una cola de hilo de bloqueo, que es una cola construida a partir de una lista vinculada, que contiene subprocesos y Wailstatus, donde WaitStatus describe el estado del hilo del nodo sucesor.
El estado es una bandera muy importante. Al construir, se establece en el valor n correspondiente. Si n! = 0, la cola de bloqueo se bloqueará todo el tiempo a menos que se interrumpa el hilo.
Cada vez que se llama el método Countdown (), State-1 se usa y el método ALEA () se usa para agregar el hilo llamando al método a la cola de bloqueo hasta que el estado == 0, y el hilo no se puede liberar.
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.