Java espera y notifica: IllegalMonitorStateException
No entiendo completamente cómo wait
y notify
(de Object
) funciona, y como resultado me veo obligado a reducir mis intentos en la siguiente sección de código.
Principal.java:
import java.util.ArrayList;
class Main
{
public static Main main = null;
public static int numRunners = 4;
public static ArrayList<Runner> runners = null;
public static void main(String[] args)
{
main = new Main();
}
Main()
{
runners = new ArrayList<Runner>(numRunners);
for (int i = 0; i < numRunners; i++)
{
Runner r = new Runner();
runners.add(r);
new Thread(r).start();
}
System.out.println("Runners ready.");
notifyAll();
}
}
Corredor.java:
class Runner implements Runnable
{
public void run()
{
try
{
Main.main.wait();
} catch (InterruptedException e) {}
System.out.println("Runner away!");
}
}
Actualmente recibo una IllegalMonitorStateException cuando llamo Main.main.wait();
, pero no entiendo por qué. Por lo que puedo ver, necesito sincronizar Runner.run
, pero al hacerlo supongo que solo notificará a un hilo, cuando la idea es notificarlos a todos.
He mirado java.util.concurrent
, pero no encuentro un reemplazo adecuado (tal vez simplemente me falta algo).
No puede wait()
hacerlo en un objeto a menos que el hilo actual sea propietario del monitor de ese objeto. Para hacer eso, debes synchronize
en él:
class Runner implements Runnable
{
public void run()
{
try
{
synchronized(Main.main) {
Main.main.wait();
}
} catch (InterruptedException e) {}
System.out.println("Runner away!");
}
}
La misma regla se aplica también a notify()
/ .notifyAll()
Los Javadocswait()
mencionan esto:
Este método solo debe ser llamado por un subproceso que sea propietario del monitor de este objeto. Consulte el
Lanza:notify
método para obtener una descripción de las formas en que un subproceso puede convertirse en propietario de un monitor.
IllegalMonitorStateException
– si el hilo actual no es el propietario del monitor de este objeto.
Y de notify()
:
Un hilo se convierte en propietario del monitor del objeto de una de tres maneras:
- Ejecutando un método de instancia sincronizado de ese objeto.
- Ejecutando el cuerpo de una
synchronized
declaración que se sincroniza en el objeto.- Para objetos de tipo
Class
, ejecutando un método estático sincronizado de esa clase.
Estás llamando a ambos wait
y notifyAll
sin usar synchronized
bloque. En ambos casos, el hilo que llama debe poseer el bloqueo en el monitor al que llama el método.
De los documentos para notify
( wait
y notifyAll
tienen documentación similar, pero consulte notify
para obtener la descripción más completa):
Este método solo debe ser llamado por un subproceso que sea propietario del monitor de este objeto. Un hilo se convierte en propietario del monitor del objeto de una de tres maneras:
- Ejecutando un método de instancia sincronizado de ese objeto.
- Ejecutando el cuerpo de una declaración sincronizada que se sincroniza en el objeto.
- Para objetos de tipo Clase, ejecutando un método estático sincronizado de esa clase.
Sólo un hilo a la vez puede poseer el monitor de un objeto.
Sólo un subproceso podrá salir wait
a la vez, notifyAll
ya que todos tendrán que adquirir el mismo monitor nuevamente, pero todos habrán sido notificados, por lo que tan pronto como el primero salga del bloque sincronizado, el siguiente adquirirá la cerradura etc