Java Wait and Notify: IllegalMonitorStateException

No entiendo completamente cómo funcionan los avisos y las notify (de Object ), y como resultado, me veo obligado a reducir mis bashs en la siguiente sección de código.

Main.java:

 import java.util.ArrayList; class Main { public static Main main = null; public static int numRunners = 4; public static ArrayList runners = null; public static void main(String[] args) { main = new Main(); } Main() { runners = new ArrayList(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(); } } 

Runner.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 a Main.main.wait(); , pero no entiendo por qué. Por lo que puedo ver, necesito sincronizar Runner.run , pero al hacerlo, supongo que solo notificará un hilo, cuando la idea es notificarlos a todos.

Miré java.util.concurrent , pero no puedo encontrar un reemplazo adecuado (tal vez me falta algo).

No puede wait() en un objeto a menos que el hilo actual sea el 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 a notifyAll() / notifyAll() también.

Los Javadocs para wait() mencionan esto:

Este método solo debe invocarse mediante un hilo que sea el propietario del monitor de este objeto. Consulte el método de notify para obtener una descripción de las formas en que un hilo puede convertirse en el propietario de un monitor.

Lanza:

IllegalMonitorStateException : si el hilo actual no es el propietario del monitor de este objeto.

Y desde notify() :

Un hilo se convierte en el propietario del monitor del objeto de tres maneras:

  • Ejecutando un método de instancia sincronizada de ese objeto.
  • Ejecutando el cuerpo de una instrucción sincronizada que se sincroniza en el objeto.
  • Para objetos de tipo Class , ejecutando un método estático sincronizado de esa clase.

Está llamando tanto a wait como a notifyAll sin usar un bloque synchronized . En ambos casos, el hilo de llamada debe poseer el locking en el monitor al que llama el método.

Desde los documentos de notify ( wait y notify notifyAll tienen documentación similar, pero consulte notify para obtener la descripción completa):

Este método solo debe invocarse mediante un hilo que sea el propietario del monitor de este objeto. Un hilo se convierte en el propietario del monitor del objeto de tres maneras:

  • Ejecutando un método de instancia sincronizada de ese objeto.
  • Ejecutando el cuerpo de una instrucción sincronizada que se sincroniza en el objeto.
  • Para objetos de tipo Clase, ejecutando un método estático sincronizado de esa clase.

Solo un hilo a la vez puede poseer el monitor de un objeto.

Solo un hilo podrá realmente salir de wait a la vez después de notifyAll ya que todos tendrán que volver a adquirir el mismo monitor, pero todos habrán sido notificados, de modo que tan pronto como el primero salga del bloque sincronizado, el siguiente adquirirá el candado, etc.