Mediante el análisis de los tres artículos anteriores, tenemos una comprensión profunda de la estructura interna y algunos conceptos de diseño de syncronizer abstractqueed, y sabemos que el syncronizer de abstracción mantiene un estado de sincronización y dos áreas de colas, que son colas sincrónicas y colas condicionales respectivamente. Usemos baños públicos como analogía. La cola de sincronización es el área principal de la cola. Si los baños públicos no están abiertos, todos los que quieran ingresar al baño tienen que hacer cola aquí. La cola de condición se establece principalmente para la espera de la condición. Imaginemos que si una persona finalmente obtiene con éxito la cerradura y entra al baño a través de la cola, pero descubre que no trae papel higiénico antes de la conveniencia. Aunque está indefenso al encontrar esta situación, también debe aceptar este hecho. En este momento, tiene que salir y preparar el papel higiénico primero (ingrese la cola de condición para esperar). Por supuesto, antes de salir, la cerradura debe liberarse para que otros puedan entrar. Después de preparar el papel higiénico (las condiciones se cumplen), tiene que volver a la cola sincrónica para hacer cola nuevamente. Por supuesto, no todas las personas que ingresan a la habitación no trajeron papel higiénico. Puede haber otras razones por las que deben interrumpir la operación y hacer cola en la cola de condición primero. Por lo tanto, puede haber múltiples colas de condición, y se establecen diferentes colas de condición de acuerdo con diferentes condiciones de espera. La cola de condición es una lista vinculada unidireccional. La interfaz de condición define todas las operaciones en la cola de condición. La clase ConditionObject dentro del Synchronizer de Abstractqueed implementa la interfaz de condición. Echemos un vistazo a qué operaciones están definidas por la interfaz de condición.
Condición de la interfaz pública {// Esperando la respuesta a la interrupción de hilo void await () lanza interruptedException; // Esperando no responder a la interrupción de hilo nula awaituninterruptable (); // Configuración de condición esperando tiempo relativo (sin giro) Long Awaitnanos (Long NanosTimeOut) arroja interruptedException; // Condición de configuración de la espera de tiempo relativo (giro) Boolean espera (la unidad de tiempo de tiempo de tiempo de tiempo de tiempo) lanza interruptedException; // Configuración de condición esperando tiempo absoluto booleano espera altilio (fecha límite de fecha) arroja interruptedException; // despertar la señal vacía del nodo de la cabeza () en la cola de condición; // despertar todos los nodos de la cola de condición void señalall (); }Aunque la interfaz de condición define tantos métodos, se divide en dos categorías en total. El método que comienza con la espera es el método que el hilo ingresa a la cola de condición y espera, y el método que comienza con la señal es el método que "despierta" el hilo en la cola de condición. Cabe señalar aquí que llamar al método de señal puede o no despertar el hilo. Cuando el hilo se despertará depende de la situación, como se discutirá más adelante, pero llamar al método de señal definitivamente moverá el hilo de la cola condicional a la cola de la cola de sincronización. En aras de la conveniencia de la narración, no nos preocuparemos por el momento. Llamaremos al método de señal el funcionamiento del hilo de cola condicional de activación. Tenga en cuenta que hay 5 tipos de métodos de espera, a saber, la interrupción de la interrupción del hilo de respuesta, la interrupción de la interrupción del hilo de no respuesta, el tiempo relativo que no espera, el tiempo relativo gira y el tiempo absoluto esperando; Solo hay dos tipos de métodos de señal, a saber, el funcionamiento de solo despertar el nodo de cabeza de cola de condición y despertar todos los nodos de la cola de condición. El mismo tipo de métodos son básicamente los mismos. Debido a las limitaciones de espacio, es imposible y no es necesario hablar con cuidado de estos métodos. Solo necesitamos comprender un método representativo y luego mirar otros métodos para comprenderlos. Entonces, en este artículo, solo hablaré sobre el método de espera y el método de señal en detalle. Otros métodos no se discutirán en detalle, pero publicarán el código fuente para su referencia.
1. Espere la respuesta a la condición de la interrupción del hilo
// Esperando en respuesta a la condición de la interrupción de hilo Public Final Void await () lanza interruptedException {// Si el hilo se interrumpe, se lanza una excepción si (thread.interrupted ()) {lanzar nueva interrupciónxception (); } // Agregue el hilo actual a la cola del nodo de cola de condición = addConditionWaIter (); // Libere completo el bloqueo antes de ingresar la condición Wait int savedState = planoRelease (nodo); int InterruptMode = 0; // El hilo ha estado esperando condicionalmente en el bucle while mientras (! IsoSyncqueue (nodo)) {// El hilo que espera condicionalmente se suspende aquí, hay varios casos en los que el hilo se despierta: // 1. El nodo directo de la cola de sincronización ha sido cancelado // 2. Establezca el estado del nodo directo de la cola de sincronización en la señal fallida // 3. El nodo actual se despierta después de que el nodo hacia adelante libera el bloqueo. // El hilo actual verifica inmediatamente si se interrumpe. Si es así, significa que el nodo cancela la condición que espera. En este momento, el nodo debe moverse fuera de la cola de condición if ((interruptMode = checkInterrupthileWaiting (nodo))! = 0) {break; }} // Después de que el hilo se despierta, adquirirá el bloqueo en modo exclusivo if (adquirired (nodo, saveDState) && interruptMode! = Throw_ie) {interruptMode = reinterrupt; } // Esta operación es principalmente para evitar que los hilos interrumpan antes de la señal, lo que no da a la desconexión de la cola de condición if (node.nextwaitar! = Null) {noinkCancelledWaIters (); } // Procesamiento de interrupción que responde al modo de interrupción if (InterruptMode! = 0) {ReportInterruptAfterWait (InterruptMode); }}Cuando un hilo llama al método de espera, el hilo actual se envolverá como un nodo de nodo y se colocará en la cola de la cola de condición. En el método AddConditionWaIter, si se encuentra que el nodo final de la cola de condición se cancela, se llamará al método OnlinkCancelledWaIters para borrar todos los nodos cancelados de la cola de condición. Este paso es la preparación para insertar nodos. Después de garantizar que el estado del nodo de cola también sea condición, se creará un nuevo nodo para envolver el hilo actual y colocarlo en la cola de la cola de condición. Tenga en cuenta que este proceso solo agrega nodos a la cola de la cola de sincronización sin suspender hilos.
Paso 2: suelte el bloqueo por completo
// Libere completo el bloqueo final int totalmente (nodo nodo) {boolean faily = true; Pruebe {// Obtenga el estado de sincronización actual int savedState = getState (); // use el estado de sincronización actual para liberar el bloqueo if (liberar (saveDState)) {fallado = falso; // Si el bloqueo se lanza correctamente, return SavedState; } else {// Si se lanza el bloqueo falla, arroje una excepción de tiempo de ejecución. }} Finalmente {// asegúrese de que el nodo esté configurado en el estado de cancelación if (fallado) {node.waitStatus = node.cancelled; }}}Después de envolver el hilo actual en un nodo y agregarlo a la cola de la cola de condición, se llama al método de información total para liberar el bloqueo. Tenga en cuenta que el método llamado TotheRelease se utiliza para liberar completamente el bloqueo, porque el bloqueo es reentrante, por lo que debe liberar el bloqueo antes de la espera condicional, de lo contrario otros no podrán adquirir el bloqueo. Si se lanza el bloqueo falla, se lanzará una excepción de tiempo de ejecución. Si el bloqueo se lanza con éxito, volverá al estado de sincronización anterior.
Paso 3: hacer condiciones de espera
// El hilo ha estado esperando en el bucle while mientras (! IsoSyncqueue (nodo)) {// Los hilos que están esperando la condición se suspenden aquí. Hay varios casos en los que se despierta el hilo: // 1. El nodo directo de la cola de sincronización ha sido cancelado // 2. Establezca el estado del nodo directo de la cola de sincronización en la señal fallida // 3. El nodo actual se despierta después de que el nodo hacia adelante libera el bloqueo. Locksupport.Park (esto); // El hilo actual se despierta inmediatamente para verificar si se interrumpe. Si es así, significa que el nodo cancela la condición que espera. En este momento, el nodo debe moverse fuera de la cola de condición if ((interruptMode = checkInterrupthileWaiting (nodo))! = 0) {break; }} // Verifique la situación de interrupción del hilo cuando la condición que espera private int checkinterrupthileWaiting (nodo de nodo) {// La solicitud de interrupción es antes de la operación de señal: throw_ie // La solicitud de interrupción es después de la operación de la señal: reinterrumpir // no se recibió la solicitud de interrupción durante este período: 0 return thread.interrumped ()? (transferFterCancelledWait (nodo)? throw_ie: reinterrupt): 0;} // transfiere el nodo que cancela la condición que espera desde la cola de condición a la cola de sincronización Boolean Boolean TransferFterCancelledWait (nodo de nodo) {// Esta operación CAS es exitosa, significa que la interrupción ocurre antes del método de señal (comparación (comparación nodo (nodo (nodo) (nodo) Node.condition, 0)) {// Después de que la modificación de estado sea exitosa, coloque el nodo en la cola de la cola de sincronización enq (nodo); devolver verdadero; } // Esto indica que la operación CAS falló, lo que indica que la interrupción ocurre después del método de señal mientras (! InonsyncQueue (nodo)) {// Si el método sinal no ha transferido el nodo a la cola de sincronización, espere a hilo.yield (); } return false;}Después de completar las dos operaciones anteriores, ingresará el bucle While. Puede ver que el bucle While primero llama a Locksupport.Park (esto) para colgar el hilo, por lo que el hilo se bloqueará aquí todo el tiempo. Después de llamar al método de señal, simplemente transfiera el nodo desde la cola condicional a la cola de sincronización. Si el hilo se despertará depende de la situación. Si encuentra que el nodo directo en la cola de sincronización se cancela al transferir un nodo, o el estado del nodo directo se actualiza a la señal fallida, ambos casos despertarán inmediatamente el hilo. De lo contrario, el hilo que ya está en la cola de sincronización no se despertará al final del método de señal, sino que esperará hasta que su nodo hacia adelante se despierta. Por supuesto, además de llamar al método de señal para despertarse, el hilo también puede responder a las interrupciones. Si el hilo recibe una solicitud de interrupción aquí, continuará ejecutándose. Puede ver que después de que el hilo se despierta, verificará inmediatamente si se despierta mediante la interrupción o el método de señal. Si se despierta por la interrupción, también transferirá este nodo a la cola de sincronización, pero se logra llamando al método TransferFterCancelledWait. Después de la ejecución final de este paso, la interrupción se devolverá y el bucle mientras se saltará.
Paso 4: Operación después de que se retira el nodo de la cola de condición
// Después de que el hilo se despierta, adquirirá el bloqueo en modo exclusivo if (adquirireed (node, saveDState) && interruptMode! = Throw_ie) {interruptMode = reinterrupts;} // Esta operación es principalmente para evitar que el hilo se interrumpe antes de la señal y no cause contacto con el cola de condición si (node.nextwaiter! UnlinkCancelledWaIters ();} // Procesamiento de interrupción que responde al modo de interrupción if (interruptMode! = 0) {ReportInterrumtAfTerWait (interruptMode);} // Después de finalizar la condición que espera, hará que el procesamiento correspondiente sea basado en la situación privada de interrupción de Informe. if (interruptMode == throw_ie) {lanzar nueva interruptaxception (); // Si el modo de interrupción se reinterrupbe, se colgará en sí mismo} else if (interruptMode == reinterrupt) {autointerrupt (); }}Cuando el hilo termina el bucle While, es decir, la condición espera, volverá a la cola de sincronización. Ya sea por devolver el método de señal o por interrupción de hilo, el nodo eventualmente estará en la cola sincrónica. En este momento, se llamará al método adquirido para realizar el funcionamiento de la adquisición de bloqueos en la cola de sincronización. Ya hemos discutido este método en detalle en el artículo de modo exclusivo. En otras palabras, después de que el nodo sale de la cola de condición, se dirige obedientemente al conjunto de bloqueos en modo exclusivo. Después de que este nodo adquiera el bloqueo nuevamente, llamará al método ReportInterrumterfterWait para responder en consecuencia en función de la situación de interrupción durante este período. Si la interrupción ocurre antes del método de señal, InterruptMode es SHONG_IE, y se lanzará una excepción después de que se obtenga el bloqueo nuevamente; Si la interrupción ocurre después del método de señal, InterruptMode se reinterrupbe y se interrumpirá nuevamente después de que se obtenga nuevamente el bloqueo.
2. Esperando la falta de respuesta a las interrupciones de los hilos
// Esperando público Final void awaitUnInterruptable () {// Agregue el hilo actual a la cola del nodo de cola de condición nodo = addConditionWaIter (); // Libere completo el bloqueo y devuelve el estado de sincronización actual int savedState = planoRelease (nodo); booleano interrumpido = falso; // Los nodos están esperando condicionalmente en el bucle while while (! Isonsyncqueue (nodo)) {// Todos los hilos en la cola de condición se suspenden aquí Locksupport.park (esto); // El hilo se despierta y descubre que la interrupción no responderá inmediatamente si (thread.interrupted ()) {interrupted = true; }} if (adquirireed (nodo, saveDState) || interrumpido) {// Responda a todas las solicitudes de interrupción aquí, si se cumplen una de las dos condiciones siguientes, se cuelgará // 1. El hilo recibe la solicitud de interrupción mientras la condición está esperando // 2. El hilo recibe la solicitud de interrupción en el método adquirido autointerrupt (); }}3. Establezca la condición de tiempo relativo esperando (sin girar)
// Establezca la condición de sincronización que espera (tiempo relativo), y no realice giro en espera que espere públicamente el largo esperanza de larga duración (largo nanoStimeOut) arroja interruptedException {// Si el hilo está interrumpido, se lanza una excepción si (horthinterrupted ()) {tire nueva interrompentexception (); } // Agregue el hilo actual a la cola del nodo de cola de condición = addConditionWaIter (); // Libere completo el bloqueo antes de ingresar la condición que espera ints saveDState = planoRelease (nodo); Long LastTime = System.nanotime (); int InterruptMode = 0; while (! Inonsyncqueue (nodo)) {// juzga si el tiempo de espera se usa si (nanoStimeOut <= 0l) {// Si se ha completado el tiempo de espera, debe ejecutar la condición de cancelación de la operación que espera transferirfterCancelledWait (nodo); romper; } // cuelgue el hilo actual por un período de tiempo, el hilo puede despertarse durante este período, o puede despertarse por sí solo con el apoyo. // Verifique primero la información de interrupción después de que el hilo se despierta if ((interruptMode = checkInterrupthileWaiting (nodo))! = 0) {break; } long Now = System.nanotime (); // tiempo de tiempo de espera menos el tiempo de espera de la condición nanoTimeOut - = ahora - orttime; LastTime = ahora; } // Después de que el hilo se despierta, adquirirá el bloqueo en modo exclusivo si (adquirir (nodo, saveDState) && interruptMode! = Throw_ie) {interruptMode = reinterrupt; } // Porque el método TransferFterCancelledWait no vacía NextWaIter, todo lo que necesita para limpiar aquí si (Node.NextWaIter! = NULL) {UnlinkCancelledWaIters (); } // Procesamiento de interrupción que responde al modo de interrupción if (InterruptMode! = 0) {ReportInterruptAfterWait (InterruptMode); } // Devuelve el tiempo de retorno restante nanoStimeOut - (System.nanotime () - LastTime);}4. Establezca la condición de tiempo relativo en espera (girar)
// Establezca la condición cronometrada que espera (tiempo relativo), realice giro que espera en la espera pública final boolean espera (largo tiempo, unidad de tiempo de tiempo) arroja interruptedException {if (unit == null) {tirar nueva nullPointerException (); } // Obtenga los milisegundos del tiempo de espera largo nanoStimeOut = unit.tonanos (tiempo); // Si el hilo se interrumpe, se lanza una excepción si (thread.interrupted ()) {lanzar nueva interruptaxception (); } // Agregue el hilo actual a la cola del nodo de cola de condición = addConditionWaIter (); // Libere completo el bloqueo antes de ingresar la condición para esperar int savedState = plottyRelease (nodo); // Obtenga los milisegundos de la hora actual por mucho tiempo lasttime = system.nanotime (); Timedout booleano = falso; int InterruptMode = 0; Mientras (! InonsyncQueue (nodo)) {// Si el tiempo de espera es TimeDout, debe realizar la operación de espera de la condición de cancelación si (nanoStimeOut <= 0l) {TimedOut = TransferAfterCancelledWait (nodo); romper; } // Si el tiempo de tiempo de espera es mayor que el tiempo de giro, el hilo se suspenderá por un período de tiempo si (nanoStimeOut> = spinfortimeOutthreshold) {locksupport.parknanos (this, nanoStimeOut); } // Después de que el hilo se despierta, verifica primero la información de interrupción if ((interruptMode = checkInterrupthileWaiting (nodo))! = 0) {break; } long Now = System.nanotime (); // El tiempo de tiempo de espera cada vez resta la hora de la condición que espera NANOSTimeOut - = ahora - LastTime; LastTime = ahora; } // Después de que el hilo se despierta, adquirirá el bloqueo en modo exclusivo si (adquirir (nodo, saveDState) && interruptMode! = Throw_ie) {interruptMode = reinterrupt; } // Dado que el método TransferFterCancelledWait no vacía a NextWaIter, todo lo que necesita limpiar aquí si (Node.NextWaIter! = NULL) {UnlinkCancelledWaIters (); } // Procesamiento de interrupción que responde al modo de interrupción if (InterruptMode! = 0) {ReportInterruptAfterWait (InterruptMode); } // Devuelve si la bandera de tiempo de espera regresa! Timedout;}5. Establezca la condición de tiempo absoluto esperando
// Establecer la condición cronometrada espera (tiempo absoluto) público final boolean boolean awaituntil (fecha límite de fecha) arroja interruptedException {if (fecha límite == null) {tirar nueva nullPointerException (); } // Obtenga los milisegundos de tiempo absoluto largo abstime = Deadline.getTime (); // Si el hilo se interrumpe, se lanza una excepción si (thread.interrupted ()) {lanzar nueva interruptaxception (); } // Agregue el hilo actual a la cola del nodo de cola de condición = addConditionWaIter (); // Libere completo el bloqueo antes de ingresar la condición Wait int savedState = planoRelease (nodo); Timedout booleano = falso; int InterruptMode = 0; while (! isoNsyncQueue (nodo)) {// if Timeout, debe realizar una operación de espera de condición de cancelación if (System.CurrentTimemillis ()> Abstime) {TimedOut = TransferFterCancelledWait (nodo); romper; } // cuelgue el hilo por un período de tiempo, durante el cual el hilo puede despertarse, o puede ser hora de despertarse por sí mismo bloqueado. // Verifique primero la información de interrupción después de que el hilo se despierta ((interruptMode = checkInterrupthileWaiting (nodo))! = 0) {break; }} // Después de que el hilo se despierta, adquirirá el bloqueo en modo exclusivo if (adquirired (nodo, saveDState) && interruptMode! = Throw_ie) {interruptMode = reinterrupt; } // Porque el método TransferFterCancelledWait no vacía NextWaIter, todo lo que necesita para limpiar aquí si (Node.NextWaIter! = NULL) {UnlinkCancelledWaIters (); } // Procesamiento de interrupción que responde al modo de interrupción if (InterruptMode! = 0) {ReportInterruptAfterWait (InterruptMode); } // Devuelve si la bandera de tiempo de espera regresa! Timedout;}6. Despierta el nodo de la cabeza en la cola condicional
// despertar el siguiente nodo en la condición cola pública pública final void señal () {// juzga si el hilo actual contiene el bloqueo if (! IsheldexClusInty ()) {tire ilegalmonitorStateException (); } Nodo First = FirstWaiter; // Si hay un cola en la cola de condición if (primero! = Null) {// despierta el nodo de la cabeza en la cola de condición dosignal (primero); }} // despertar el nodo de la cabeza en la condición de la cola privada void dosignal (nodo primero) {do {// 1. Mueva la referencia de FirstWaiter One by One if ((FirstWaIter = First.NextWaIter) == NULL) {lastWaIter = null; } // 2. Vacíe la referencia del nodo sucesor del nodo Head.nextwaitrer = null; // 3. Transfiera el nodo de la cabeza a la cola de sincronización, y es posible despertar el hilo después de que se complete la transferencia // 4. Si la operación de transferencia de Forsignal falla, despierta el siguiente nodo} while (! TransferForSignal (First) && (FirstWaIter)! = NULL);} // Transfiere el nodo especificado de la cola de condición a la cola de sincronización final BooleanForSignal (nodo de nodo) {// establece el estado de espera de la condición a 0 if (! Node.condition, 0)) {// Si la operación para actualizar el estado falla, devuelve falso directamente // puede ser que el método transferFterCancelledWait haya cambiado el estado primero, lo que hace que esta operación CAS falle return False; } // Agregue este nodo a la cola del nodo de cola de sincronización p = enq (nodo); int ws = p.waitstatus; if (ws> 0 || El nodo directo está en el estado de cancelación // 2. El estado del nodo de actualización de actualización es la operación de señal fallida Locksupport.unpark (node.thread); } return true;}Se puede ver que el núcleo final del método de señal es llamar al método de transferencia de la señalización. En el método de transferencia de Forsignal, primero use la operación CAS para establecer el estado del nodo desde la condición a 0, y luego llame al método ENQ para agregar el nodo a la cola de la cola de sincronización. Vemos la siguiente declaración de juicio IF. Esta declaración de juicio se usa principalmente para determinar cuándo se despertará el hilo. Si se producen estas dos situaciones, el hilo se despertará inmediatamente. Una es cuando se descubre que el estado del nodo anterior se cancela, y el otro es cuando el estado del nodo anterior no se actualiza. Ambos casos despertarán inmediatamente el hilo, de lo contrario se realizará simplemente transfiriendo el nodo desde la cola condicional a la cola de sincronización, y no despertará inmediatamente el hilo en el nodo. El método de señalización es más o menos similar, excepto que brota a través de todos los nodos en la cola condicional y los transfiere a la cola sincrónica. El método de transferencia de nodos todavía llama al método de transferencia de señalización.
7. Despierta todos los nodos de la cola de condición
// despertar todos los nodos detrás de la condición cola pública pública final void señalall () {// juzga si el hilo actual contiene el bloqueo if (! IsheldexClusInty ()) {tire ilegalmonitorStateException (); } // Obtenga el nodo del nodo de encabezado de cola de condición primero = FirstWaitrer; if (primero! = null) {// despertar todos los nodos de la cola de condición dosignalall (primero); }} // despertar todos los nodos de la condición cola privada void dosignalall (nodo primero) {// Primero vacía las referencias del nodo de encabezado y el nodo de cola dentwaitrer = firstWaIter = null; do {// Obtenga la referencia del nodo sucesor primero = first.nextwaiter; // vaciar la referencia posterior del nodo a transferir primero.nextwaitrer = null; // transferir el nodo desde la cola condicional a la transferencia de la cola de sincronización (primero); // señala la referencia al siguiente nodo primero = siguiente; } while (primero! = null);}En este punto, todo nuestro análisis del código fuente de Synchronizer de Abstractqueed se acabó. Creo que a través de estos cuatro análisis, todos pueden dominar y comprender mejor los AQ. Esta categoría es de hecho muy importante porque es la piedra angular de muchas otras categorías de sincronización. Debido al nivel limitado y la capacidad de expresión del autor, si no hay declaraciones claras o una comprensión inadecuada, corregirlos a tiempo y discutir y aprender juntos. Puede dejar un mensaje para leer el problema a continuación. Si necesita el código fuente de comentarios de AQS, también puede comunicarse con el autor para solicitarlo.
Nota: Todo el análisis anterior se basa en JDK1.7, y habrá diferencias entre diferentes versiones, los lectores deben prestar atención.
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.