Análisis del código fuente de CountdownLatch - Await (), el contenido específico es el siguiente
El artículo anterior habló sobre cómo usar CountdownLatch. Este artículo hablará sobre el principio de Await () desde el nivel del código fuente.
Ya sabemos que la esperanza puede mantener el hilo actual en un estado de bloqueo hasta que el recuento de pestillos sea cero (o interrupción del hilo).
A continuación se muestra su código fuente.
end.await (); ↓ public void Await () lanza interruptedException {sync.acquiresharedInterruptiblemente (1);}Sync es la clase interna de CountdownLatch. Aquí está su definición.
La sincronización de clase final estática privada se extiende abstractQueueedSynChronizer {...}Hereda el Syncronizer de Abstractqueed. AbstractqueedSynchronizer Esta clase pertenece a una clase muy importante en los hilos de Java.
Proporciona un marco para implementar cerraduras de bloqueo y sincronizadores relacionados (como señales, eventos, etc.) que dependen de las colas de espera FIFO.
Continúa y salta a la clase de Synchronizer de AbstractqueedEed.
syncquiresharedInterruptiblemente (1); ↓ Public Final void adquirido a InterruptUly (int arg) // abstractQueueedSynChronizer lanza interruptedException {if (thread.interrupted ()) tirar nueva interruptedException (); if (tryacquireshared (arg) <0) doacquiresharedInterruptiblemente (arg);}Hay dos juicios aquí. Primero, determine si el hilo se interrumpe y luego realiza el siguiente juicio. Aquí miramos principalmente el segundo juicio.
protegido int tryacquireshared (int adquirir) {return (getState () == 0)? 1: -1;}Cabe señalar que el método TryAcquireshared se implementa en Sync.
Aunque hay implementaciones de TI en AbstractqueedSynChronizer, la implementación predeterminada es lanzar una excepción.
TryAcquireshared Este método se utiliza para consultar si el estado del objeto actual puede adquirir el bloqueo.
Podemos ver que en sincronización, devolvemos el valor INT correspondiente determinando si el estado es 0.
Entonces, ¿qué significa el estado?
/*** El estado de sincronización. */ Estado privado volátiles int;
El código anterior muestra claramente que el estado representa el estado de sincronización.
Cabe señalar que State usa la palabra clave volátil para modificarla.
La palabra clave volátil puede garantizar que la modificación del estado se actualice a la memoria principal de inmediato. Cuando otros hilos necesitan leer, el nuevo valor se leerá en la memoria.
Es decir, la visibilidad del estado está garantizada. Son los últimos datos.
¿Cuál es el estado que viene aquí?
Aquí tenemos que echar un vistazo al constructor de CountdownLatch.
CountDownLatch final = new CountdownLatch (2); ↓ public CountdownLatch (int count) {if (count <0) tirar nueva ilegalargumentException ("count <0"); this.sync = new Sync (Count);} ↓ sinc (int count) {setState (count);}Resulta que los números en el constructor se utilizan para establecer el estado.
Entonces tenemos estado == 2 aquí. TryAcquireshared devuelve -1. Ingrese a continuación
doacquiresharedInterruptiblemente (arg); ↓ vacío privado doacquiresharedInterruptiblemente (int arg) lanza interruptedException {nodo final nodo = addWaIter (node.shared); booleano fallido = verdadero; intente {for (;;) {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 ()) tirar nueva interruptaxception (); }} finalmente {if (fallido) cancelacquire (nodo); }}Ok, este código es un poco largo, y se llaman varias funciones. Veamoslo uno por uno.
Aparece un nuevo nodo de clase en la primera línea.
El nodo es una clase interna en la clase AQS (AbstractqueedSynChronizer), que define una estructura de cadena. Como se muestra a continuación.
+------+anterior+-----++-----+Cabeza | | <---- | | <---- | | | cola +----- + +----- + +----- +
Recuerda esta estructura.
También hay un método en la primera línea de Code AddWaIter (node.shared).
AddWaIter (node.shared) //node.shared significa que el nodo está en modo compartido ↓ nodo privado addwaitrer (modo nodo) {nodo nodo = new node (thread.currentThread (), mode); // prueba la ruta rápida de ENQ; Copia de seguridad de ENQ completo en el nodo de falla Pred = cola; // cola de nodo volátil transitoria privada; if (pred! = null) {node.prev = pred; if (compareAndsettail (pred, nodo)) {pred.next = node; nodo de retorno; }} enq (nodo); nodo de retorno;}Primero, se construye un nodo y se almacena el hilo actual. El modo es un modo compartido.
Tail significa que el final de la cola de la cola que espera es nula en este momento. Entonces Pred == nulo entra en ENQ (nodo);
enq (nodo) ↓ nodo privado ENQ (nodo del nodo final) {for (;;) {nodo t = cola; if (t == null) {// debe inicializar if (compareAndsethead (new node ())) tail = head; } else {node.prev = t; if (compareAndsettail (t, nodo)) {t.next = node; regresar t; }}}}La misma cola es nula, ingrese a la línea de estado de comparación.
CompareandsThead (nuevo nodo ()) ↓/*** Campo de cabeza CAS. Utilizado solo por Enq. */Private Final Boolean CompareandEndsethead (actualización del nodo) {return unsafe.compareandswapobject (this, headoffset, null, actualización);}Esta es una operación CAS. Si la cabeza es nula, la cabeza de la cola de espera se establecerá en el valor de actualización, que es un nodo nuevo.
cola = cabeza; Entonces la cola ya no es nula en este momento. Ingrese el siguiente ciclo.
Esta vez, primero apunte el puntero anterior del nodo a la cola, luego establezca el nodo en la cola a través de una operación CAS y devuelva la cola de la cola, es decir, el nodo.
El modelo de la cola de espera cambia de la siguiente manera
+ ------+ antes +----------------------------------------------------------------------------------- ---------------------------------------------------------------------------------------------------
Ok, cuando llegas aquí, el método de espera regresa, es un hilo igual al nodo del hilo actual.
Regrese a DoacquiresharedInterruption (int Arg) e ingrese el siguiente bucle.
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, suponiendo que ese estado sea aún mayor que 0, entonces r <0 en este momento, por lo tanto, ingrese el método debería serparkafterfailedacquire.
Debe tener una tartaza (P, P, nodo) ↓ Booleano estático privado debe tener una tartaza de Cailedacquire (nodo pred, nodo de nodo) {int ws = pred.waitstatus; if (ws == node.signal) // static final int señal = -1; / * * Este nodo ya ha establecido el estado pidiendo una versión * que lo indique, por lo que puede estacionar de manera segura. */ return true; if (ws> 0) { / * * El predecesor fue cancelado. Saltar a los predecesores y * reintento indicado. */ do {node.prev = pred = pred.prev; } while (preds.waitstatus> 0); pred.next = nodo; } else { / * * WailStatus debe ser 0 o propagar. Indique que * necesitamos una señal, pero todavía no estacionamos. La persona que llama deberá * volver a intentarlo para asegurarse de que no pueda adquirir antes del estacionamiento. */ compareandsetwaitstatus (pred, ws, node.signal); } return False;} ↓/*** CAST CAS WaitStatus de un nodo. */Private static final Boolean compareandSetWaitStatus (nodo de nodo, int espere, int actualización) {return unsafe.compareandswapint (nodo, waitStatusoffset, esperanza, actualización);}Puede ver que debe ser unirfterfailedacquire también llega a comparar a los estatus.
CompareandSetWaitStatus establece el WaTStatus de previo en nodo.signal.
Node.Signal significa que los hilos en los nodos posteriores deben ser incomparables (similar a ser despertados). Este método devuelve falso.
Después de este ciclo, el modelo de cola se convierte en el siguiente estado
+-----------------------------------------------------------------------------------------------
Debido a que debería hacer que la tartaza de CailedAcque devuelva falsas, ya no miraremos las siguientes condiciones. Continúe el bucle para (;;).
Si el estado aún es mayor que 0, ingrese nuevamente a que deba hacer una tartaza.
Esta vez, porque WaitStatus in Head es Node.Signal, debe ser elfarfterfailedacquire devuelve verdadero.
Esta vez necesito ver el método ParkandCheckInterrupt.
Private Final Boolean ParkandCheckInterrupt () {Locksupport.park (this); return thread.interrupted (); }Ok, el hilo no se interrumpe, por lo que devuelve falso. Continúe el bucle para (;;).
Si el estado siempre es mayor que 0 y el hilo no se interrumpe, entonces siempre está en este bucle. Es decir, los árbitros mencionados en el artículo anterior que siempre han sido reacios a anunciar el final del juego.
Entonces, ¿en qué circunstancias estallará el bucle? Es decir, ¿bajo qué circunstancias indicarán menos de 0? Explicaré el próximo artículo.
Para resumir, el método ALEA () es realmente inicializar una cola, agregar el hilo que debe esperar (estado> 0) a una cola y usar WaTStatus para marcar el estado del hilo del nodo sucesor.
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.