Comprensión profunda del mecanismo de notificación de espera
Sabemos que el mecanismo de notificación de espera/notificación de Java se puede utilizar para implementar la comunicación entre subprocesos. Esperar indica que el subproceso está esperando y llamar a este método hará que el subproceso se bloquee hasta que otro subproceso llame al método notificar o notificar a todos. El modelo clásico de productor y consumidor se implementa mediante un mecanismo de espera/notificación. En este artículo, analizaremos este mecanismo en profundidad y comprenderemos los principios detrás de él.
Estado del subproceso
Antes de comprender el mecanismo de espera/notificación, primero familiarícese con los distintos ciclos de vida de los subprocesos de Java. Son iniciales (nuevos), ejecutables, bloqueantes, en espera, en espera cronometrada, terminados, etc. (Ubicado en la clase de enumeración java.lang.Thread.State).
La siguiente es una breve descripción de estos estados, se pueden encontrar descripciones detalladas en dichas notas.
Necesitamos conocer el estado y la relación de conversión entre los hilos anteriores.
Tanto WAITING como TIMED_WAITING pondrán el hilo en estado de espera. La diferencia es que TIMED_WAITING regresará solo después del tiempo de espera, mientras que WAITING debe esperar hasta que cambien las condiciones.
El único requisito previo para entrar en el estado de bloqueo es esperar a adquirir el bloqueo de sincronización. La anotación de Java aclara que solo hay dos situaciones en las que un hilo puede entrar en un estado de bloqueo: una está esperando a que un bloque o método entre en sincronización, y la otra es un bloque o método que vuelve a entrar en sincronización después de llamar a wait(). método. Esto se explicará en detalle a continuación.
La implementación de la clase de bloqueo no hará que el hilo entre en estado de bloqueo. La capa inferior del bloqueo llama al método LockSupport.park() para poner el hilo en estado de espera.
Caso de uso de espera/notificación
Analicemos primero mediante un ejemplo.
El método wait() puede poner el hilo en estado de espera y notify() puede activar el estado de espera. Este mecanismo de sincronización encaja bien en el modelo productor-consumidor: un consumidor consume un recurso y un productor lo produce. Cuando faltan recursos, el consumidor llama al método wait() para bloquearse y esperar a que el productor produzca; el productor llama a notify/notifyAll() para despertar al consumidor para el consumo.
Clase pública ThreadTest {
Objeto final estático privado obj = new Object();
Indicador booleano estático privado = false
Público Static void main(String[] args) lanza InterruptedException {
Thread Produce = new Thread(new Produce(), "Produce");
Thread Consume = new Thread(new Consume (), "Consumir");
consume.start();
thread.sleep(1000);
produce.start(); p>
p>
}
//Subproceso productor
Clase estática Produce implementos Ejecutable {
@override
ejecución pública no válida (){
Sincronización (objeto){
System.out.println("Ingrese el hilo del productor");
system out . println(" producción ");
flag = true
obj .
System.out.println("Salir del hilo del productor" );
p>
}
}
}
//Hilo del consumidor
Estático class Consume implementa Runnable {
@Override
Public void run(){
Sincronización(objeto){
System.out. println("Ingrese el hilo del consumidor");
System.out.println("Esperando la bandera 1: " bandera);
Y (!flag){
Probar{
System.out.println("Aún no producido, esperando");
obj . wait();
system out . println(" finalizar espera ");
} catch (InterruptedException e) {
e . printstacktrace(); }
System .out.println("Esperando bandera 2:" bandera);
System.out.println("Consumo");
Sistema .out.println("Salir del hilo del consumidor ");
}
}
}
}
Comprenda el orden de los resultados de salida y luego comprenda el uso básico de esperar/notificar. Hay que saber los siguientes puntos:
Esto no se refleja en el ejemplo, pero un punto muy importante es que el método de espera/notificación debe llamarse en el monitor del objeto, es decir, al llamar Para utilizar estos métodos, primero debe obtener el objeto Lock. De lo contrario, obtendrá una IllegalMonitorStateException.
A juzgar por los resultados de salida, después de que el productor llama a notificar (), el consumidor no se despierta inmediatamente, sino que se despierta después de que el productor sale del bloque de sincronización. De hecho, es fácil de entender: el método de sincronización sincronizada (bloque) solo permite que un hilo salga y el consumidor no pueda entrar.
Tenga en cuenta que después de despertar al usuario, se ejecuta desde detrás del método wait() (donde estaba bloqueado), en lugar de comenzar nuevamente desde el bloque sincronizado.
Información
En esta sección, analizamos la relación entre esperar/notificar y el estado del hilo. Obtenga más información sobre el ciclo de vida del subproceso.
Según el diagrama de transición de estado del hilo anterior, cuando se llama al método wait (), el hilo entrará en el estado de espera. Después de ser notificado (), no se ejecutará inmediatamente, pero entrará. la cola de bloqueo para esperar la adquisición.
Para cada objeto, tiene su propia cola de espera y cola de bloqueo. Tomemos como ejemplo a los productores y consumidores anteriores. Usamos el objeto obj como bloqueo de objeto para que coincida con el diagrama. El proceso interno es el siguiente
Cuando el subproceso A (consumidor) llama al método de espera (), el subproceso A libera el bloqueo, entra en estado de espera y se une a la cola de espera del objeto bloqueado.
Después de que el subproceso B (productor) obtiene el bloqueo, llama al método de notificación para notificar a la cola de espera sobre el objeto bloqueado, lo que hace que el subproceso A ingrese a la cola de bloqueo desde la cola de espera.
Después de que el subproceso A ingresa a la cola de bloqueo, hasta que el subproceso B libera el bloqueo, el subproceso A compite por el bloqueo y continúa ejecutándose desde el método wait().