Red de conocimiento del abogados - Preguntas y respuestas jurídicas - Cómo usar esperar, notificar y notificar todo correctamente en Java

Cómo usar esperar, notificar y notificar todo correctamente en Java

esperar, notificar y notificarTodas estas palabras clave reservadas que a menudo se usan en subprocesos múltiples a menudo no se toman en serio durante el desarrollo real. Este artículo describe el uso de estas palabras clave.

En Java, puede utilizar esperar, notificar y notificar a todos

para lograr la comunicación entre subprocesos. . Por ejemplo, si tiene dos subprocesos en su programa Java: un productor y un consumidor, entonces el productor puede notificar al consumidor que comience a consumir datos, porque el búfer de cola

Hay contenido para consumir (no vacío). En consecuencia, el consumidor puede notificar al productor que puede comenzar a generar más datos porque el búfer ya no está lleno después de haber consumido algunos datos.

Podemos usar wait() para pausar un hilo bajo ciertas condiciones. Por ejemplo, en el modelo productor-consumidor, el subproceso productor debe pausarse cuando el búfer está lleno y el subproceso consumidor cuando el búfer está vacío. Si algunos subprocesos están esperando que se activen ciertas condiciones, cuando esas condiciones sean verdaderas, puede usar notificar y notificar a todos

para notificar a esos subprocesos en espera que comiencen a ejecutarse nuevamente. La diferencia es que notificar solo notifica a un hilo y no sabemos qué hilo recibirá la notificación, mientras que notifyAll

notificará a todos los hilos en espera. En otras palabras, si solo hay un hilo esperando un semáforo, tanto notificar como notificar a todos notificarán a este hilo. Pero si varios subprocesos están esperando esta señal, notificar solo notificará a uno de ellos, y otros subprocesos no recibirán ninguna notificación, y notifyAll despertará a todos los subprocesos en espera.

En este artículo aprenderá cómo utilizar esperar, notificar y notificar a todos para implementar la comunicación entre subprocesos para resolver el problema productor-consumidor. Si desea obtener más información sobre los problemas de sincronización de subprocesos múltiples en Java, le recomiendo leer "La concurrencia de Java en la práctica | Práctica de concurrencia de Java" de Brian Goetz. Su viaje con subprocesos múltiples en Java no estará completo sin leer este libro. Este es uno de mis libros más recomendados para desarrolladores de Java.

Cómo usar Wait

Aunque los conceptos de esperar y notificar son muy básicos y ambos son funciones de la clase Object, no es sencillo usarlos para escribir código. Si les pide a los candidatos que escriban código a mano durante la entrevista,

use esperar y notificar para resolver los problemas entre productores y consumidores, estoy casi seguro de que la mayoría de ellos se sentirán perdidos o cometerán algunos errores, como como en el error La palabra clave

synchronized se usa en todas partes, esperar no se usa en el objeto correcto o no se sigue el método de codificación estándar. Para ser honesto, este problema es un dolor de cabeza para los programadores que no los usan con frecuencia.

La primera pregunta es, ¿cómo usamos wait() en el código? Debido a que wait() no es una función de la clase Thread, no podemos usar

Thread.call(). De hecho, a muchos programadores de Java les gusta escribir de esta manera porque están acostumbrados a usar Thread.sleep (), por lo que intentarán usar wait()

para lograr el mismo propósito, pero pronto lo harán. descubrir Esto no resuelve el problema sin problemas. El método correcto es utilizar esperar en el objeto compartido entre varios subprocesos. En el problema productor-consumidor, el primer objeto compartido es la cola del búfer.

La segunda pregunta es, dado que debemos llamar a esperar en una función u objeto sincronizado, ¿qué objeto debería sincronizarse? La respuesta es que el objeto que desea bloquear debe estar sincronizado, es decir, el objeto que se comparte entre varios subprocesos. En el problema productor-consumidor, lo que se debe sincronizar es la cola del búfer. (Creo que hay un problema con el texto original en inglés... No debería haber un signo de interrogación al final de la oración, de lo contrario no tiene sentido...)

Llame siempre espere y notifique en un bucle, no en una instrucción If

Ahora que sabe que esperar siempre debe llamarse en un contexto sincronizado y en un objeto compartido por varios subprocesos, la siguiente pregunta que debe recordar es. que siempre debes llamarlo en un contexto sincronizado.

ciclo while en lugar de llamar a esperar en la declaración if. Debido a que el subproceso está esperando bajo ciertas condiciones; en nuestro caso, "si la cola del búfer está llena, entonces el subproceso productor debe esperar

", puede escribir intuitivamente una declaración if. Pero hay algunos problemas sutiles con la declaración if, lo que significa que su hilo puede activarse por error incluso si no se cumple la condición. Entonces, si no usas un bucle while nuevamente después de que se despierta el hilo

para verificar si se cumple la condición de despertar, tu programa puede salir mal; por ejemplo, el productor continúa generando cuando el buffer está datos completos, o cuando el búfer está vacío, el consumidor

comienza a dimensionar los datos. Así que recuerda, ¡usa siempre esperar en un bucle while en lugar de una declaración if! Recomendaría leer Effective Java, que es la mejor referencia sobre cómo utilizar esperar y notificar correctamente.

Basado en el conocimiento anterior, la siguiente es la plantilla de código estándar para usar las funciones de espera y notificación:

// El modismo estándar para llamar al método de espera en Java sincronizado (objeto compartido) { while (condición) {sharedObject.wait(); // (Libera el bloqueo y vuelve a adquirirlo al despertar) } // realiza la acción según la condición, por ejemplo, tomar o poner en cola }

Como dije antes , el propósito de usar wait en el bucle while es verificar continuamente si se cumple la condición antes y después de que se despierte el hilo. Si las condiciones no han cambiado y la notificación de activación de Notify llega antes de que se llame a esperar, entonces no se garantiza que este hilo se despierte, lo que puede causar un problema de interbloqueo.

Ejemplos de Java wait(), notify(), notifyAll()

A continuación proporcionamos un programa de muestra que utiliza esperar y notificar. En este programa, utilizamos algunas de las convenciones de codificación mencionadas anteriormente. Tenemos dos hilos, llamados

PRODUCER (productor) y CONSUMER (consumidor). Heredan las clases Productor y Consumidor respectivamente, y tanto Productor como

Consumidor heredan la clase Hilo. La lógica del código que Productor y Consumidor quieren implementar está dentro de la función run(). El hilo principal inicia los hilos productor y consumidor y declara una LinkedList como una cola de búfer (en Java, LinkedList implementa la interfaz de cola). El productor continúa insertando números enteros aleatorios en

LinkedList en un bucle infinito hasta que LinkedList esté lleno.

Verificamos esta condición en la declaración de bucle while(queue.size ==

maxSize). Tenga en cuenta que hemos utilizado la palabra clave sincronizada en el objeto de la cola antes de realizar esta condición de verificación, por lo que otros subprocesos no pueden cambiar la cola mientras verificamos la condición. Si la cola está llena, el subproceso PRODUCER continuará esperando antes de que el subproceso CONSUMIDOR consuma cualquier número entero en la cola y utilice notificar para notificar al subproceso PRODUCER. En nuestro ejemplo, esperar y notificar se utilizan en el mismo objeto compartido.

import java.util.LinkedList; import java.util.Queue; import java.util.Random; /** * Programa Java simple para demostrar cómo usar el método esperar, notificar y notificar a todos () Java resolviendo el problema del consumidor productor * * @author Javin Paul */ public class ProducerConsumerInJava { public static void main(String args[]) { System.out.println("Cómo utilizar el método de espera y notificación en el sistema Java"); .out.println("Resolviendo el problema del consumidor del productor"); Queue buffer = new LinkedList<>(); int maxSize = 10; new Producer(buffer, maxSize, "PRODUCER" consumidor = new); Consumer(buffer, maxSize, "CONSUMER"); productor.start(); consumer.start(); } } /** * El hilo del productor seguirá produciendo valores para el consumidor * al consumidor. cuando la cola está llena * y usa el método notify() para enviar una notificación al consumidor * Thread. @author WINDOWS 8 * */ class Producer extends Thread { private Queue queue; > cola, int maxSize, nombre de cadena){ super(nombre); this.queue = cola; this.maxSize = maxSize } @Override public void run() { while (true) { sincronizado (cola) { while (cola. size() == maxSize) { try { System.out .println("La cola está llena, " + "Hilo del productor esperando que " + "el consumidor tome algo de la cola"); ex) { ex.printStackTrace();

Aleatorio aleatorio = new Random(); int i = random.nextInt(); System.out.println("Produciendo valor: " + i); queue.add(i); ** * El hilo del consumidor consumirá valores de la cola compartida * También usará el método wait() para esperar si la cola está * vacía. También usará el método de notificación para enviar * una notificación al hilo del productor después de consumir valores *. de la cola. * * @author WINDOWS 8 * */ class Consumidor extiende Thread { cola privada; private int maxSize; cola cola, int maxSize, nombre de cadena){ super(nombre); .queue = cola; this.maxSize = maxSize; } @Override public void run() { while (true) { sincronizado (cola) { while (queue.isEmpty()) { System.out.println("La cola está vacía, " + "El subproceso del consumidor está esperando" + " a que el subproceso del productor ponga algo en la cola"); intente { queue.wait(); } catch (Exception ex) { ex.printStackTrace(); } } System.out.println( "Consumiendo valor: " + queue.remove()); queue.notifyAll(); } } } }

Para comprender mejor este programa, le sugiero que lo ejecute en modo de depuración. Una vez que inicie el programa en modo de depuración, se detendrá en el subproceso PRODUCER o

CONSUMER, dependiendo de qué subproceso esté ocupando la CPU. Debido a que ambos subprocesos tienen condiciones de espera (), definitivamente se detendrán y luego podrá ejecutar este programa y ver qué sucede

(Es muy probable que genere lo que mostramos arriba). También puede utilizar los botones Paso a paso y Paso a paso en Eclipse para comprender mejor lo que sucede entre varios subprocesos.

Puntos clave de este artículo:

1. Puede utilizar funciones de espera y notificación para lograr la comunicación entre subprocesos. Puede usarlos para implementar la comunicación entre múltiples subprocesos (>3).

2. Utilice siempre esperar, notificar y notificar a todo en funciones u objetos sincronizados; de lo contrario, la máquina virtual Java generará IllegalMonitorStateException.

3. Utilice siempre esperar en un bucle while en lugar de una instrucción if. De esta manera, el bucle verifica la condición de espera antes y después de que el hilo entre en suspensión y maneja la notificación de activación si la condición realmente no ha cambiado.

4. Utilice siempre la espera en objetos compartidos entre varios subprocesos (cola de búfer en el modelo productor-consumidor).

5. Según las razones mencionadas anteriormente, es más probable utilizar notifyAll() en lugar de notify().

Estos son todos los puntos clave sobre cómo usar esperar,

notificar y notificar a todo en Java. Sólo debes utilizar estas funciones si sabes lo que vas a hacer; de lo contrario, existen muchas otras soluciones a los problemas de sincronización en Java.

Por ejemplo, si desea utilizar el modelo productor-consumidor, también puede utilizar BlockingQueue, que se encargará de todos los problemas de seguridad de los subprocesos y del control de procesos por usted. Si desea que un hilo espere comentarios de otro hilo antes de continuar, también puede usar CycliBarrier o CountDownLatch. Si solo desea proteger un determinado recurso, también puede utilizar Semaphore.