Java espera y notifica: IllegalMonitorStateException

Resuelto skeggse asked hace 13 años • 2 respuestas

No entiendo completamente cómo waity 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).

skeggse avatar Aug 20 '11 02:08 skeggse
Aceptado

No puede wait()hacerlo en un objeto a menos que el hilo actual sea propietario del monitor de ese objeto. Para hacer eso, debes synchronizeen é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 notifymétodo para obtener una descripción de las formas en que un subproceso puede convertirse en propietario de un monitor.

Lanza:

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 synchronizeddeclaración que se sincroniza en el objeto.
  • Para objetos de tipo Class, ejecutando un método estático sincronizado de esa clase.
dlev avatar Aug 19 '2011 19:08 dlev

Estás llamando a ambos waity notifyAllsin usar synchronizedbloque. 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( waity notifyAlltienen documentación similar, pero consulte notifypara 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, notifyAllya 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

Jon Skeet avatar Aug 19 '2011 19:08 Jon Skeet