En este capítulo, presentaremos el método de espera/despertar de hilo. El contenido involucrado incluye:
1. Introducción a Wait (), Notify (), NotifyAll () y otros métodos
2. Espera () y notificar ()
3. Espera (tiempo de espera largo) y notificar ()
4. Espera () y notifyall ()
5. ¿Por qué se notifican (), Wait () y otras funciones definidas en el objeto, no en el hilo?
Introducción a Wait (), notificar (), notifyAll () y otros métodos
En Object.java, se definen interfaces como Wait (), notify () y notifyall (). La función de Wait () es dejar que el hilo actual ingrese a un estado de espera, y Wait () también permitirá que el hilo actual libera el bloqueo que contiene. El papel de notificar () y notifyAll () es despertar el hilo de espera en el objeto actual;
Los detalles de la API sobre Waiting/Wake en la clase de objetos son los siguientes:
Notificar () - despertar un solo hilo esperando en este monitor de objeto.
NotifyAll () - despertar todos los hilos esperando en este monitor de objeto.
Wait (): coloque el hilo actual en un "estado de espera (bloqueo)" y "hasta que otros hilos llamen al método notify () o notifyall () de este objeto", y el hilo actual se despierta (ingresado al " Estado listo ").
espera (tiempo de espera largo): deje que el hilo actual esté en un "estado de espera (bloqueo)" y "hasta que otros hilos llamen al método notify () o notifyall () de este objeto, o excedan la cantidad especificada de tiempo", y el hilo actual se despierta (ingrese "listo").
espera (tiempo de espera largo, int nanos): coloque el hilo actual en un "estado de espera (bloqueo)" hasta que otro hilo llame al método notify () o notifyall () de este objeto, o algún otro hilo interrumpe el hilo actual, o la cantidad real de tiempo ha excedido ", y el hilo actual se despierta (ingresado al" estado listo ").
2. Espera () y notificar () ejemplos
El siguiente es un ejemplo para demostrar "Wait () y notificar () juntos".
La copia del código es la siguiente:
// código fuente de waitest.java
clase Thread extiende el hilo {
Public Thread (nombre de cadena) {
super (nombre);
}
public void run () {
sincronizado (esto) {
System.out.println (thread.currentThread (). GetName ()+"llamar notify ()");
// despierta el hilo de espera actual
notificar();
}
}
}
clase pública waitest {
public static void main (string [] args) {
Thinta t1 = new thinda ("t1");
sincronizado (t1) {
intentar {
// Comienza "Hilo T1"
System.out.println (thread.currentThread (). GetName ()+"inicio t1");
t1.start ();
// El hilo principal espera a que T1 se despierte a través de Notify ().
System.out.println (Thread.CurrentThread (). GetName ()+"Wait ()");
t1.wait ();
System.out.println (thread.currentThread (). GetName ()+"continuar");
} capt (interruptedException e) {
E.PrintStackTrace ();
}
}
}
}
Resultados de ejecución:
La copia del código es la siguiente:
Inicio principal T1
Espera principal ()
T1 llamado notificar ()
Principal continuar
Descripción de los resultados:
La siguiente figura ilustra el flujo de "hilo principal" y "hilo T1".
(01) Tenga en cuenta que el "hilo principal" en la figura representa el "hilo principal principal". "Hilo T1" representa "Hilo T1" comenzó en Waittest. Y "bloquear" representa "el bloqueo sincrónico del objeto T1".
(02) "El hilo principal" crea un nuevo "hilo T1" a través de un nuevo thrave ("T1"). Luego, el "bloqueo sincrónico del objeto T1" se obtiene a través de sincronizado (T1). Luego llame a T1.Start () para comenzar "Hilo T1".
(03) "El hilo principal" ejecuta T1.Wait () para liberar "Bloqueo del objeto T1" y ingresa al "Bloqueo (Bloqueo)". Espere los hilos en los objetos T1 para despertarlo a través de notificar () o notifyall ().
(04) Después de que se ejecuta "Hilo T1", el "bloqueo del objeto actual" se obtiene a través de Sincronized (esto); el "hilo principal".
(05) Después de que se complete "Subpuste T1", suelte el "Bloqueo del objeto actual". Inmediatamente después, el "hilo principal" adquiere el "bloqueo del objeto T1" y luego se ejecuta.
Para el código anterior? Un amigo una vez preguntó: t1.wait () debería hacer que "hilo t1" espera;
Antes de responder a esta pregunta, echemos un vistazo a un párrafo sobre esperar en el documento JDK:
La copia del código es la siguiente:
Hace que el hilo actual espere hasta que otro hilo invoque el método notify () o el método notifyall () para este objeto.
En otras palabras, este método se comporta exactamente como si simplemente realice la llamada de espera (0).
El hilo actual debe poseer el monitor de este objeto. puede volver a obTre la propiedad del monitor y reanuda la ejecución.
El significado en chino es más o menos:
Hace que el "hilo actual" espere hasta que otro hilo llame a notificar () o notifyall () para despertar el hilo. ¡En otras palabras, este método tiene el mismo efecto que Wait (0)! (Suplementario, para el método de espera (Long Millis), cuando Millis es 0, significa una espera infinita hasta que se despierta notify () o notifyall ()).
Cuando el "hilo actual" llama a Wait (), debe tener el bloqueo de sincronización para el objeto. Después de que el hilo llame a Wait (), se lanzará el bloqueo; Luego, el hilo continúa esperando hasta que vuelva a visitar el "bloqueo de sincronización para este objeto" y luego puede continuar funcionando.
Nota: En la explicación de JDK, se dice que la función de Wait () es hacer que el "hilo actual" espera, y el "hilo actual" se refiere al hilo que se ejecuta en la CPU.
Esto también significa que aunque T1.Wait () es el método Wait () llamado a través de "Subproceso T1", el lugar donde se llama T1.Wait () está en "Main Thread Main". El hilo principal debe ser el "hilo actual", es decir, el estado en ejecución, antes de que se pueda ejecutar t1.wait (). ¡Por lo tanto, el "hilo actual" en este momento es el "hilo principal principal"! Por lo tanto, t1.wait () es hacer el "hilo principal" esperar, no "hilo T1"!
3. Espera (tiempo de espera largo) y notificar ()
esperar (tiempo de espera largo) colocará el hilo actual en un "estado de espera (bloqueo)" y "hasta que otros hilos llamen al método notify () o notifyall () de este objeto, o excedan la cantidad de tiempo especificada", y el El hilo actual se despierta (ingresado) "listo").
El siguiente ejemplo demuestra el tiempo de espera de espera (tiempo de espera largo), y el hilo se despierta.
La copia del código es la siguiente:
// Código fuente de WaitTimeOuttest.java
clase Thread extiende el hilo {
Public Thread (nombre de cadena) {
super (nombre);
}
public void run () {
System.out.println (thread.currentThread (). GetName () + "ejecutar");
// Un círculo vicioso, que se ejecuta continuamente.
Mientras (verdadero)
}
}
clase pública waittimeuttest {
public static void main (string [] args) {
Thinta t1 = new thinda ("t1");
sincronizado (t1) {
intentar {
// Comienza "Hilo T1"
System.out.println (thread.currentThread (). GetName () + "inicio t1");
t1.start ();
// El hilo principal espera a que T1 se despierte a través de notificar () o notifyall (), o retrasos superiores a los 3000 ms;
System.out.println (thread.currentThread (). GetName () + "llamar a la espera");
t1.wait (3000);
System.out.println (thread.currentThread (). GetName () + "continuar");
} capt (interruptedException e) {
E.PrintStackTrace ();
}
}
}
}
Resultados de ejecución:
La copia del código es la siguiente:
Inicio principal T1
Llamada principal espera
T1 Run // Después de aproximadamente 3 segundos ... salida "Principal continuar"
Principal continuar
Descripción de los resultados:
La siguiente figura ilustra el flujo de "hilo principal" y "hilo T1".
(01) Tenga en cuenta que el "hilo principal" en la figura representa el hilo principal de WaitTimeouttest (es decir, el hilo principal). "Hilo T1" representa el hilo T1 iniciado en Waittest. Y "bloquear" representa "el bloqueo sincrónico del objeto T1".
(02) El hilo principal principal ejecuta t1.start () para iniciar "hilo T1".
(03) El hilo principal principal ejecuta t1.wait (3000), y en este momento, el hilo principal ingresa al "estado de bloqueo". Es necesario "el hilo utilizado para el bloqueo del objeto T1 para despertarlo a través de notify () o notifyall ()" o "después de 3000 ms de tiempo de espera", el principal de hilo principal ingresa al "estado listo" y luego puede ejecutarse.
(04) Después de que se ejecuta "Hilo T1", ingresa a un bucle muerto y continúa ejecutándose.
(05) Después de que el tiempo de espera sea de 3000 ms, el principal de hilo principal ingresará al "estado listo" y luego ingresará al "estado en ejecución".
4. Espera () y notifyall ()
A través del ejemplo anterior, sabemos que notify () puede despertar un solo hilo esperando en este monitor de objeto.
A continuación, demostramos el uso de NotifyAll () a través de un ejemplo;
La copia del código es la siguiente:
clase pública NotifyAllTest {
objeto estático privado obj = nuevo objeto ();
public static void main (string [] args) {
Thinta t1 = new thinda ("t1");
Thinta t2 = new thinda ("t2");
Thinta t3 = new thinda ("t3");
t1.start ();
t2.start ();
t3.start ();
intentar {
System.out.println (Thread.CurrentThread (). GetName ()+"Sleep (3000)");
Thread.sleep (3000);
} capt (interruptedException e) {
E.PrintStackTrace ();
}
sincronizado (obj) {
// El hilo principal está esperando el despertar.
System.out.println (Thread.CurrentThread (). GetName ()+"notifyAll ()");
obj.notifyall ();
}
}
La clase estática Thread extiende el hilo {
Public Thread (nombre de cadena) {
super (nombre);
}
public void run () {
sincronizado (obj) {
intentar {
// Resultado de salida de impresión
System.out.println (thread.currentThread (). GetName () + "esperar");
// despierta el hilo de espera actual
obj.wait ();
// Resultado de salida de impresión
System.out.println (thread.currentThread (). GetName () + "continuar");
} capt (interruptedException e) {
E.PrintStackTrace ();
}
}
}
}
}
Resultados de ejecución:
La copia del código es la siguiente:
T1 espera
sueño principal (3000)
T3 espera
T2 espera
Principal notifyall ()
T2 continúa
T3 Continuar
T1 Continuar
Descripción de los resultados:
Consulte el diagrama de flujo a continuación.
(01) 3 hilos "T1", "T2" y "T3" fueron creados y comenzaron en el hilo principal.
(02) El hilo principal duerme durante 3 segundos durante el sueño (3000). Durante el sueño principal del hilo durante 3 segundos, suponemos que los tres hilos "T1", "T2" y "T3" están funcionando. Tome "T1" como ejemplo. También esperará a que otros hilos los despertarán a través de nofity () o nofityall ().
(03) El hilo principal duerme durante 3 segundos y luego corre. Ejecute obj.notifyall () para despertar el hilo de espera en OBJ, es decir, despierta los tres hilos "T1", "T2" y "T3". Inmediatamente después de que se ejecuta el hilo principal (OBJ), el hilo principal libera el "bloqueo OBJ". De esta manera, "T1", "T2" y "T3" pueden obtener el "Bloqueo OBJ" y continuar ejecutándose.
5. ¿Por qué se notifican (), Wait () y otras funciones definidas en el objeto, no en el hilo?
Funciones como Wait (), notificar () en objeto, como sincronizado, funcionarán en "Bloqueo de sincronización de objetos".
Wait () hará que el "hilo actual" espere. ¡No poder correr!
Ok, después de que el hilo llame a Wait (), liberará el "bloqueo sincrónico" de su bloqueo; Ahora, piense en una pregunta: ¿Qué es notificar () basado en despertar el hilo de espera? ¿O cuál es la correlación entre Wait () y notificar ()? La respuesta es: basada en "bloqueo de sincronización de objetos".
El hilo responsable de despertar el hilo de espera (lo llamamos "hilo de despertar"), solo adquiere el "bloqueo de sincronización del objeto" (el bloqueo de sincronización aquí debe ser el mismo que el bloqueo de sincronización del hilo de espera), y llamadas notificar () o después del método notifyall (), el hilo de espera se puede despertar. Aunque el hilo de espera se despierta; Debe esperar hasta que el hilo de activación libere el "bloqueo de sincronización del objeto" antes de poder obtener el "bloqueo de sincronización del objeto" y continuar ejecutándose.
En resumen, notify (), Wait () se basa en "Lock Syncronous", que se mantiene en bloqueos de objetos, y cada objeto tiene y solo uno! Es por eso que funciones como notify (), wait () se definen en la clase de objeto, no en la clase de hilo.