Foros del Web » Programación para mayores de 30 ;) » Java »

[Solucionado]Threads: ¿Cómo usar wait() y notify()?

Estas en el tema de [Solucionado]Threads: ¿Cómo usar wait() y notify()? en el foro de Java en Foros del Web. Estoy probando la alternancia de hilos, y no soy capaz de devolver a la vida un hilo que está en wait() Éste es el código ...
  #1 (permalink)  
Antiguo 02/02/2010, 18:46
Avatar de Xerelo  
Fecha de Ingreso: mayo-2009
Mensajes: 2.175
Antigüedad: 15 años, 6 meses
Puntos: 306
[Solucionado]Threads: ¿Cómo usar wait() y notify()?

Estoy probando la alternancia de hilos, y no soy capaz de devolver a la vida un hilo que está en wait()

Éste es el código que estoy utilizando

Cita:
public class AlterarOrdenHilos {

public static void main(String[] args) throws InterruptedException {

System.out.println("Hilo principal " + Thread.currentThread().toString());

FirstThread f = new FirstThread();
ThirdThread t = new ThirdThread();

f.start();
t.start();
f.join();

System.out.println("Fin del padre2");
try {
//t.interrupt();
//Thread.currentThread().notify();


} catch (Exception e) {}

}

}

class FirstThread extends Thread {
public void run () {
try {
System.out.println(" First thread starts running.");
sleep(10000);
System.out.println(" First thread finishes running.");
} catch (Exception e) {}
}
}


class ThirdThread extends Thread {
public void run () {
try {
System.out.println(" Third thread starts running.");
wait();
System.out.println(" Third thread finishes running.");
} catch (Exception e) {}
}
}
He probado alternativamente las dos opciones en rojo, pero nunca consigo que el ThirdThread muestre el mensaje de salida. Con Thread.currentThread().notify() además obtengo un error IllegalMonitorStateException a pesar de los try/catch, lo mismo que si intento poner al ThirdThreat en wait() desde el man (t.wait() ) en vez de hacerlo desde el run.

¿Cómo puedo reactivarlo? ¿se puede parar desde el main?

Saludos y gracias.

Última edición por Xerelo; 03/02/2010 a las 04:35
  #2 (permalink)  
Antiguo 02/02/2010, 19:42
Avatar de Xerelo  
Fecha de Ingreso: mayo-2009
Mensajes: 2.175
Antigüedad: 15 años, 6 meses
Puntos: 306
Respuesta: Threads: ¿Cómo usar wait() y notify()?

[URL="http://java.sun.com/docs/books/tutorial/essential/concurrency/interrupt.html"]http://java.sun.com/docs/books/tutorial/essential/concurrency/interrupt.html[/URL]

Leyendo el tutorial de Sun, dice que al llamar a interrupt() produce una InterruptedException que saca al hilo del estado wait(), y que normalmente (aunque no siempre) continúa con la ejecución del hilo.

He cambiado la clase poniendo a ésta forma

Cita:
class ThirdThread extends Thread {
public void run () {
try {
System.out.println(" Third thread starts running.");
wait();
} catch (Exception e) { }
System.out.println(" Third thread finishes running.");
}
}
He comprobado que al volver ejecutar el interrupt() pasa por el catch, y efectivamente continúa con la ejecución del hilo apareciendo el mensaje deseado, pero todavía no he conseguido que funcione utilizando el notify().

Seguiré investigando, pero se agradece cualquier ayuda o idea.
  #3 (permalink)  
Antiguo 03/02/2010, 02:03
 
Fecha de Ingreso: octubre-2003
Mensajes: 3.578
Antigüedad: 21 años, 1 mes
Puntos: 51
Respuesta: Threads: ¿Cómo usar wait() y notify()?

Algunos comentarios:
.- Nunca debes dejar los catch(Exception) en blanco (se conocen como "blind catches"), ya que entonces nunca sabrás si se produjo un error. Si no los tuvieras, te habrías dado cuenta que el wait() del ThirdThread ya producía una excepción.
.- La excepcion IllegalMonitorStateException se suele producir cuando estas intentando ejecutar un wait/notify sobre un objeto sin tener el acceso sincronizado, lo cual no te deja. Por eso hay que tener sincronizado el acceso cuando se llama a wait()/notify().
.- Al terminar el proceso principal, el programa "se muere" bruscamente, así que hay que tener cuidado con los mensajes que se imprimen justo al final. Puede ser que el tercer Thread termine bien pero no llegues a ver el mensaje por que el programa ha cerrado la salida estandar antes de vaciar el buffer. Por eso le he puesto un sleep() antes de acabar.
.- El programa lo he dejado que funcione, pero no quiere decir que sincronizar directamente sobre el objeto Thread sea lo más recomendable ni lo único que funcione.
.- El interrupt es para interrumpir los Threads bruscamente y lo habitual es usarlo cuando estan en un sleep que quieres cortar. Si estan en un wait se usa notify, que no genera excepciones.
Código PHP:
package test;

public class 
App
{

  public static 
void main(String[] argsthrows InterruptedException
  
{

    
System.out.println("Hilo principal " Thread.currentThread().toString());

    
FirstThread f = new FirstThread();
    
ThirdThread t = new ThirdThread();

    
f.start();
    
t.start();
    
f.join();

    try
    {
      
synchronized (t)
      {
        
t.notify();
      }
    }
    catch (
Exception e)
    {
      
e.printStackTrace();
    }
    try
    {
      
Thread.sleep(5000);
    }
    catch (
Exception e)
    {
      
e.printStackTrace();
    }
    
System.out.println("Fin del padre2");
  }

}

class 
FirstThread extends Thread
{
  public 
void run()
  {
    try
    {
      
System.out.println(" First thread starts running.");
      
sleep(5000);
      
System.out.println(" First thread finishes running.");
    }
    catch (
Exception e)
    {
      
e.printStackTrace();
    }
  }
}

class 
ThirdThread extends Thread
{
  public 
synchronized void run()
  {
    try
    {
      
System.out.println(" Third thread starts running.");
      
wait();
      
System.out.println(" Third thread finishes running.");
    }
    catch (
Exception e)
    {
      
e.printStackTrace();
    }
  }

__________________
Para obtener respuestas, pregunta de forma inteligente o si no, pregunta lo que quieras que yo contestaré lo que me dé la gana.

Última edición por GreenEyed; 03/02/2010 a las 05:52
  #4 (permalink)  
Antiguo 03/02/2010, 04:34
Avatar de Xerelo  
Fecha de Ingreso: mayo-2009
Mensajes: 2.175
Antigüedad: 15 años, 6 meses
Puntos: 306
Respuesta: Threads: ¿Cómo usar wait() y notify()?

Muchas gracias por toda la información que me has dado, funciona perfectamente.

-Normalmente no uso blind catches, pero a fuerza de ir probando cosas alguno se me quedó por el camino
-Pensaba que el synchronized sólo era necesario cuando se intentaba acceder a un recurso compartido, y como en este caso no había tal recurso supuse que no haría falta.
-Por la posibilidad de que al morir el hilo principal no se ejecutaran correctamente los otros, hice alguna prueba en la que aparentemente sí funcionaban, así que lo he probado sin el sleep que comentas y me ha funcionado. Creo que es interesante la posibilidad de que hilos sigan en ejecución aunque el principal haya muerto, pero si puede ser un problema supongo que entre los join() y la creación de un bucle en el principal que compruebe la existencia de hilos abiertos, el problema estaría resuelto.
-Sólo eran pruebas para entender funcionamiento, me imaginaba que en realidad habrá formas más elegantes y efectivas de hacerlo.
-Lo que me ha sorprendido es que la llamada al notify() sea a través del método en espera y no desde el que se está ejecutando, supongo que será porque el principal no está definido como synchronized.
  #5 (permalink)  
Antiguo 03/02/2010, 06:01
 
Fecha de Ingreso: octubre-2003
Mensajes: 3.578
Antigüedad: 21 años, 1 mes
Puntos: 51
Respuesta: [Solucionado]Threads: ¿Cómo usar wait() y notify()?

.- Supongo que era para las pruebas, pero como todos estaban así... Yo no los dejo así ni siquiera, e incluso especialmente, para las pruebas, por que es cuando más excepciones me saltan :).
.- Synchronised es necesario por que estas accediendo a un recurso compartido: el bloqueo del objeto.
.- El problema en si no es que los otros threads se mueran antes de terminar, cosa que no puede ocurrir a no ser que hagas un Sysm.exit() o sea threads del tipo daemon, si no que acaben tan rapido que la JVM no vacíe el buffer de la salida estandar antes de salir. No es habitual, pero a mi me ha pasado alguna vez cuando los tiempos van muy ajustados y dependiendo de la carga etc. De hecho, dado que no podias llamar al notify, si el wait hubiera funcionado tu programa no habría acabado nunca y se habría quedado "colgado" con thread esperando en un wait.
.- No me queda muy claro lo que explicas en el último punto, por que el notify no se llama a través de un "método en espera". Quizá te refieres a que se llama al método del objeto que esta "en espera" (wait), pero es lo "lógico" puesto que el notify se ha de hacer sobre el mismo objeto de bloqueo (monitor) que esta esperando. Si no, ¿Como sabrías qué objetos tienes que "despertar"?

El de la sincronización y los Thread es un tema complejo, así que es bueno hacer estos experimentos y confirmar o descartar lo que creemos que sabemos. Yo aun me encuentro de vez en cuando cosas que me sorprenden .
__________________
Para obtener respuestas, pregunta de forma inteligente o si no, pregunta lo que quieras que yo contestaré lo que me dé la gana.
  #6 (permalink)  
Antiguo 03/02/2010, 07:11
Avatar de Xerelo  
Fecha de Ingreso: mayo-2009
Mensajes: 2.175
Antigüedad: 15 años, 6 meses
Puntos: 306
Respuesta: [Solucionado]Threads: ¿Cómo usar wait() y notify()?

A pesar de mi explicación, has entendido perfectamente lo que quería decir en el último punto.

Sin embargo, justo lo que argumentas como lógico (para mí también lo es), al leer la web de Chuidiang, no me queda tan claro que lo sea.

[URL="http://www.chuidiang.com/java/hilos/wait_y_notify.php"]http://www.chuidiang.com/java/hilos/wait_y_notify.php[/URL]

Cita:
Si varios hilos van llamando a wait() quedan bloqueados y en una lista de espera, de forma que el primero que llamó a wait() es el primero de la lista y el último es el útlimo.

Cada llamada a notify() despierta al primer hilo en la lista de espera,
Por lo que entiendo, la llamada a un notify() no despierta al hilo que ejecuta el notify(), sino al primero de una lista, siendo muchas las ocasiones en las que no podamos prever cual será. Tal y como yo entiendo eso, cualquier hilo puede llamar al notify() que despierte al hilo parado, y por eso pensé que lo más lógico sería que fuese el principal.

Incluso comenta que estos notify() pueden acumularse, y ser utilizados por un hilo que entra en wait() posteriormente al lanzamiento del notify().

Cita:
Si hacemos varios notify() antes de que haya hilos en espera, quedan marcados todos esos notify(), de forma que los siguientes hilos que hagan wait() no se quedaran bloqueados.
La verdad es que el tema de los hilos es bastante complejo, así que es mejor verlo pasito a pasito y probar todo lo que se te ocurra, por absurdo que parezca.
  #7 (permalink)  
Antiguo 03/02/2010, 14:03
 
Fecha de Ingreso: octubre-2003
Mensajes: 3.578
Antigüedad: 21 años, 1 mes
Puntos: 51
Respuesta: [Solucionado]Threads: ¿Cómo usar wait() y notify()?

Cualquier hilo puede "llamar al notify()", a lo que me refiero es que "hay que llamar al notify() del objeto bloqueado".
Es decir, tu llamas a obj.notify() desde el Thread que quieras, pero obj ha de ser el objeto sobre el que los otros Thread han hecho el obj.wait() (aunque sea implicitamente con un wait() si lo hace el mismo obj en uno de sus metodos como era en el caso que tu pusiste).

Respecto a lo que comentas de la explicación de Chuidiang, lamento contradecirla respetuosamente pero esas dos cosas no son ciertas:
.- El Thread que se despierta cuando haces un notify() es "aleatorio y depende de la implementación de la JVM". Supongo que Chuidiang hizo las pruebas en una JVM que lo habia implementado como una cola FIFO y supuso que era así siempre, pero no es cierto como se puede comprobar echando un vistazo al API:
http://java.sun.com/j2se/1.5.0/docs/...tify%28%29
Cita:
If any threads are waiting on this object, one of them is chosen to be awakened. The choice is arbitrary and occurs at the discretion of the implementation.
.- wait/notify no llevan "contadores de llamada" así que puedes llamar al notify() 250.000 veces antes del wait(), que el wait() seguirá bloqueandose y sólo se despertará cuando alguien llame a un notify() después. Es facilísimo de comprobar en el mismo ejemplo que tienes, simplemente pones notify(); antes del wait(); y si fuera cierto que recuerda las llamadas a notify() no se tendría que bloquear. No he encontrado un sítio específico donde esté documentado por que lo aprendí hace muchos años, pero así es.
.- Como comentario extra, la tecnica que usa Chuidiang en sus ejemplos de usar synchronized a nivel de método no se suele recomendar, excepto para bloqueos muy simples, ya que no permite un control refinado de las zonas de bloqueo (se bloquea siempre el objeto entero para cualquier método sincronizado). Es más recomendable usar bloques synchronized sobre objetos, simples, aunque no sirvan más que para eso (típica variable miembro Object lock = new Object(); .

Y como extra un enlace interesante sobre errores habituales:
http://www.javamex.com/tutorials/syn...notify_3.shtml

Ah, y no olvides echarle un vistazo a la libreria Concurrent que viene con el JDK a partir de Java 5, ya que implementa la solución a muchos problemas comunes sin tener que llegar al bajo nivel del wait/notify.. etc.
__________________
Para obtener respuestas, pregunta de forma inteligente o si no, pregunta lo que quieras que yo contestaré lo que me dé la gana.
  #8 (permalink)  
Antiguo 03/02/2010, 17:31
Avatar de Xerelo  
Fecha de Ingreso: mayo-2009
Mensajes: 2.175
Antigüedad: 15 años, 6 meses
Puntos: 306
Respuesta: [Solucionado]Threads: ¿Cómo usar wait() y notify()?

Veo que el tema de los threads tiene mucha más miga de la que suponía, así que voy a seguir avanzando en otros temas, aunque echándole un vistazo de vez en cuando al último enlace que has puesto, parece una página muy interesante.

Gracias por todas las explicaciones.

Etiquetas: wait
Atención: Estás leyendo un tema que no tiene actividad desde hace más de 6 MESES, te recomendamos abrir un Nuevo tema en lugar de responder al actual.
Respuesta




La zona horaria es GMT -6. Ahora son las 01:16.