A menudo exploro. Con respecto a notificar y notificar a todos en Java, las personas a menudo tienen las siguientes afirmaciones:
Notify solo notificará a un objeto que espera, mientras que NotifyAll notificará a todos los objetos que esperan, y todos los objetos continuarán ejecutándose.
Y parece que hay ejemplos para probarlo. Se puede decir que la declaración anterior es correcta o no. La razón es que uno de ellos es muy importante. La declaración oficial es la siguiente:
Espera, notificar, notificar todo:
Este método solo debe ser llamado por un hilo que es el propietario de este monitor de objeto. Un hilo puede convertirse en el propietario de este monitor de objeto de una de las tres maneras:
Ejecutando el método de instancia síncrona de este objeto.
El cuerpo de la declaración sincronizada se ejecuta ejecutando la declaración sincronizada en este objeto.
Para los objetos de clase de tipo, puede ejecutar métodos estáticos síncronos de la clase.
Solo un hilo tiene un monitor de objeto a la vez.
La declaración anterior se extrae de Javadoc. Esto significa que en la llamada, el monitor de objeto (es decir, el bloqueo) debe mantenerse, lo que podemos entender que se ejecuta dentro del método sincronizado. Entonces, el significado implícito de esta declaración es que si desea continuar el bloque de código contenido en el bloque de sincronización, debe volver a adquirir el bloqueo. Esta oración se describe en Javadoc:
esperar
Este método hace que el hilo actual (llamado t) se coloque en el conjunto de espera del objeto y luego abandone todos los requisitos de sincronización en este objeto. Para fines de programación de subprocesos, el hilo T está deshabilitado y está en hibernación antes de que ocurra una de las siguientes cuatro situaciones:
Algún otro hilo llama al método de notificación de este objeto, y el subproceso t se selecciona opcionalmente como el hilo de despertar.
Algún otro hilo llama al método de notificación de este objeto.
Algún otro hilo interrumpe el hilo T.
Aproximadamente se ha alcanzado el tiempo real especificado. Sin embargo, si el tiempo de espera es cero, el tiempo real no se tiene en cuenta y el hilo esperará hasta que se obtenga la notificación.
Luego, el hilo T se elimina del conjunto de espera del objeto y la programación de hilos se realiza nuevamente. Luego, el hilo compite con otros hilos de manera convencional para obtener el derecho a sincronizar el objeto; Una vez que se obtiene el control sobre el objeto, todas sus declaraciones de sincronización en el objeto se restaurarán a su estado anterior, que es llamar a la espera
La situación cuando el método es. Thread t luego regresa de la llamada al método de espera. Por lo tanto, al regresar del método de espera, el estado de sincronización del objeto y el hilo t es exactamente el mismo que cuando se llama al método de espera.
Es decir, el bloqueo debe ser readquisito, de modo que para notificar a todos, se han notificado todos los hilos. Sin embargo, estos hilos competirán, y solo un hilo adquirirá con éxito el bloqueo. Antes de que se ejecute este hilo, otros hilos deben esperar (pero no hay necesidad de notificar la notificación aquí, porque se notifica todo, y solo es necesario adquirir el bloqueo). Existe el siguiente código que puede reproducir este fenómeno.
Primero, defina una clase de hilo que pueda ejecutarse, de la siguiente manera:
objero final estático privado obj = nuevo objeto (); La clase estática R implementos runnables {int i; R (int i) {this.i = i; } public void run () {try {sincronizado (obj) {System.out.println ("Thread->" + I + "Waiting"); obj.wait (); System.out.println ("Thread->" + i + "Running"); Thread.sleep (30000); }} catch (Exception e) {E.PrintStackTrace (); }}} Presta atención al interior del método de ejecución anterior. Después de esperar (), imprimimos una oración y luego detenemos el código actual durante 30 segundos. Con respecto al método de sueño, se describe de la siguiente manera:
El hilo no pierde la propiedad de ningún monitor.
Es decir, la cerradura todavía se mantiene.
Luego, defina un método principal para ejecutar estos hilos de la siguiente manera:
Hilo [] rs = nuevo hilo [10]; para (int i = 0; i <10; i ++) {rs [i] = new Thread (new R (i)); } para (hilo r: rs) {r.start (); } Thread.sleep (5000); sincronizado (obj) {obj.notifyall (); }Definimos 10 hilos y luego los ejecutamos todos. Debido a que hay espera, 10 hilos esperarán después de imprimir "Iniciar ejecución". Luego, el método principal llama a notificar. La salida aquí aparecerá de la siguiente manera:
Hilo-> 0 Esperando hilo-> 4 esperando hilo-> 5 esperando hilo-> 3 esperando hilo-> 2 esperando hilo-> 1 esperando hilo-> 6 esperando hilo-> 7 esperando hilo-> 8 esperando hilo-> 9 esperando hilo-> 9 corriendo
... No hay otra salida en 30 segundos
En la salida anterior, después de esperar, solo un hilo emite la instrucción "en ejecución" y por un período de tiempo (30 segundos aquí), no habrá otra salida. Es decir, otros subprocesos no emitirán entre los bloqueos que poseen el código actual.
La conclusión final es: si el hilo de espera quiere continuar corriendo, debe cumplir con dos condiciones:
El otro hilo notifica o notifica todo ha sido notificado, y el hilo actual ha sido notificado.
Después de competir con otros hilos para bloquear, se obtuvieron dos condiciones con éxito para el bloqueo, y ninguna de ellas faltaba. De hecho, en el nivel de implementación, notifique y notifique a todo logra el mismo efecto, y un hilo continuará funcionando. Pero notificar todo está exento. La necesidad de notificar otros hilos después de que el hilo se ejecuta, porque ha sido notificado. Cuándo usar notificar y cuándo usar notifyall, depende de la situación real.
Lo anterior es una compilación de la información para Java notificar y notificar todo. Continuaremos agregando información relevante en el futuro. ¡Gracias por su apoyo para este sitio web!