Resumen de programación concurrente de Java 9: Condición detallada

Para obtener más detalles, consulte: Explicación detallada del mecanismo de notificación / espera de señal en espera y de condición

En general, la espera y la notificación / notificación de Object deben cooperar con el monitor de objetos para completar el mecanismo de espera / notificación entre subprocesos;

Y Condition and Lock cooperan para completar el mecanismo de notificación de espera;

El primero está en el nivel inferior de Java y el segundo está en el nivel del lenguaje, con mayor capacidad de control y escalabilidad .

Además de las diferentes formas de utilizar los dos, todavía existen muchas diferencias en las características funcionales :

  1. La condición puede admitir interrupciones sin respuesta, pero no mediante el modo Objeto;
  2. La condición puede admitir varias colas de espera (nuevos objetos de condición múltiple), mientras que el modo Objeto solo puede admitir una;
  3. La condición puede admitir la configuración del tiempo de espera, pero el objeto no;

 

1. El principio de implementación de condiciones

1. El principio de realización de la cola de espera :

Se pasa la construcción de un objeto de condición lock.newCondition(),y este método crea realmente un objeto ConditionObject , que es una clase interna de AQS;

En la implementación del mecanismo de bloqueo, AQS mantiene una cola de sincronización internamente. Si es un bloqueo exclusivo, las colas de todos los subprocesos que no logran adquirir el bloqueo se insertan en la cola de sincronización . De manera similar, la condición también se usa internamente en la De la misma manera, y uno se mantiene internamente. En  espera de la cola , todos los subprocesos que llaman al método condition.await se agregarán a la cola de espera y el estado del subproceso se convierte al estado de espera.

Hay dos variables miembro en ConditionObject: firstWaiter y lastWaiter :

/** First node of condition queue. */
private transient Node firstWaiter;
/** Last node of condition queue. */
private transient Node lastWaiter;

Se puede ver que ConditionObject administra la cola de espera sosteniendo los punteros de cabeza y cola de la cola de espera.

La clase Node reutiliza la clase Node en AQS, y su estado de nodo y los atributos relacionados se pueden ver en la implementación de AQS;

La clase Node tiene tal atributo:

// El nodo sucesor
Node nextWaiter;

La cola de espera es una cola unidireccional , y cuando dije AQS antes, sabía que la cola de sincronización era una cola bidireccional .

  • Después de llamar al método condition.await, los hilos se insertan en la cola de espera a su vez, como se muestra en la figura, las referencias de hilo en la cola son Thread-0, Thread-1, Thread-2 ... Thread-8;
  • La cola de espera es una cola de un solo sentido.

Puede llamar al método lock.newCondition () varias veces para crear varios objetos de condición , es decir, un bloqueo puede contener varias colas de espera .

El método anterior de usar Object en realidad significa que solo puede haber una cola de sincronización y una cola de espera en el monitor Object Object.

El bloqueo en el paquete concurrente tiene una cola de sincronización y varias colas de espera

2. El principio de espera:

Después de que el hilo actual llama al método condition.await (), el hilo actual liberará el bloqueo y luego lo agregará a la cola de espera hasta que sea signal / signalAll, lo que hará que el hilo actual se mueva de la cola de espera a la sincronización cola hasta que se obtenga el bloqueo Luego volverá del método de espera, o se interrumpirá cuando se interrumpa mientras espera .

2.1. ¿Cómo agregar el hilo actual a la cola de espera?

El nodo encapsulado por el hilo actual se puede insertar en la cola de espera mediante la inserción de la cola . Al mismo tiempo, se puede ver que la cola de espera es una cola en cadena sin un nodo principal . Antes de aprender AQS, sabíamos que la sincronización cola era un nodo principal. Cola de cadena , esta es una diferencia entre los dos.

2.2 ¿El proceso de liberación de la cerradura?

Llame al método de liberación del método de plantilla de AQS para liberar el estado de sincronización de AQS y reactivar el subproceso al que hace referencia el nodo sucesor del nodo principal en la cola de sincronización . Si la liberación tiene éxito, volverá normalmente, y si falla, un se lanzará una excepción.

2.3. ¿Cómo puedo salir del método de espera?

El hilo actual se interrumpe o se llama al método condition.signal / condition.signalAll. Una vez que el nodo actual se mueve a la cola de sincronización  , este es un requisito previo para que el hilo actual salga del método await.

Después de salir del bucle while llamará a acquireQueued(node, savedState)este método en el momento en que dijo que la introducción de la implementación subyacente de AQS, si está interesado puede ir a ver este artículo , el papel de este método es en el proceso de hilado de hilos de intentos continuos para adquirir el estado de sincronización, Hasta que tenga éxito (el hilo se bloquea) . Esto también muestra que la salida del método de espera debe ser el bloqueo que ha obtenido la referencia de condición (asociación) .

3. El principio de realización de señal y señal Todos:

Llamar a la señal o señal Todos los métodos de condición pueden mover el nodo con el tiempo de espera más largo en la cola de espera a la cola de sincronización , de modo que el nodo pueda tener la oportunidad de obtener el bloqueo. Según la cola de espera es el primero en entrar, primero en salir (FIFO), el nodo principal de la cola de espera debe ser el nodo con el tiempo de espera más largo, es decir, cada vez que se llama al método de señal de condición, el nodo principal se mueve a la cola síncrona.

El requisito previo para llamar a la señal de condición es que el subproceso actual ha adquirido el bloqueo . Este método hará que el nodo principal en la cola de espera, es decir, el nodo con el tiempo de espera más largo, se mueva a la cola de sincronización y se mueva al cola de sincronización antes de que haya una posibilidad de hacer que el subproceso en espera se haya despertado, es decir, volver del método LockSupport.park (this) en el método de espera, para tener la oportunidad de hacer que el subproceso que llamó al método de espera salga con éxito .

La diferencia entre el método signalAll y el método signal se refleja en: método doSignalAll . Ya sabemos que el método doSignal solo operará en el nodo principal de la cola de espera. Este método solo se da cuenta de que cada nodo en la cola de espera se mueve a la cola de sincronización Es decir, "notifica" a cada hilo que actualmente llama al método condition.await ().

4. Combinando los dos pensamientos:

El mecanismo de notificación de espera se puede realizar utilizando los métodos await y signal / signalAll proporcionados por la condición , y este mecanismo puede resolver el problema más clásico que es el " problema del productor y del consumidor".

Esta imagen es muy importante. ¡Se puede decir que comprende y recuerda esta imagen!

 

Supongo que te gusta

Origin blog.csdn.net/ScorpC/article/details/113859719
Recomendado
Clasificación