web-dev-qa-db-fra.com

Java Attendez et notifiez: IllegalMonitorStateException

Je ne comprends pas complètement comment wait et notify (de Object) fonctionnent, et par conséquent je suis obligé de réduire mes tentatives dans la section de code suivante .

Main.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();
  }
}

Runner.Java:

class Runner implements Runnable
{
  public void run()
  {
    try
    {
      Main.main.wait();
    } catch (InterruptedException e) {}
    System.out.println("Runner away!");
  }
}

Actuellement, j'obtiens une exception IllegalMonitorStateException lors de l'appel de Main.main.wait();, mais je ne comprends pas pourquoi. D'après ce que je peux voir, j'ai besoin de synchroniser Runner.run, Mais ce faisant, je suppose qu'il ne notifierait qu'un seul thread, lorsque l'idée est de les notifier tous.

J'ai regardé Java.util.concurrent, Mais je ne trouve pas de remplacement approprié (peut-être que je manque juste quelque chose).

35
skeggse

Vous ne pouvez pas wait() sur un objet à moins que le thread actuel ne possède le moniteur de cet objet. Pour ce faire, vous devez synchronize dessus:

class Runner implements Runnable
{
  public void run()
  {
    try
    {
      synchronized(Main.main) {
        Main.main.wait();
      }
    } catch (InterruptedException e) {}
    System.out.println("Runner away!");
  }
}

La même règle s'applique également à notify()/notifyAll().

Les Javadocs pour wait() mentionnent ceci:

Cette méthode ne doit être appelée que par un thread propriétaire du moniteur de cet objet. Voir la méthode notify pour une description des façons dont un thread peut devenir propriétaire d'un moniteur.

Lance:

IllegalMonitorStateException - si le thread actuel n'est pas le propriétaire du moniteur de cet objet.

Et de notify() :

Un thread devient le propriétaire du moniteur de l'objet de trois manières:

  • En exécutant une méthode d'instance synchronisée de cet objet.
  • En exécutant le corps d'une instruction synchronized qui se synchronise sur l'objet.
  • Pour les objets de type Class, en exécutant une méthode statique synchronisée de cette classe.
61
dlev

Vous appelez à la fois wait et notifyAll sans utiliser de bloc synchronized. Dans les deux cas, le thread appelant doit possède le verrou sur le moniteur sur lequel vous appelez la méthode.

À partir des documents pour notify (wait et notifyAll ont une documentation similaire mais référez-vous à notify pour la description la plus complète):

Cette méthode ne doit être appelée que par un thread propriétaire du moniteur de cet objet. Un thread devient le propriétaire du moniteur de l'objet de trois manières:

  • En exécutant une méthode d'instance synchronisée de cet objet.
  • En exécutant le corps d'une instruction synchronisée qui se synchronise sur l'objet.
  • Pour les objets de type Class, en exécutant une méthode statique synchronisée de cette classe.

Un seul thread à la fois peut posséder le moniteur d'un objet.

Un seul thread pourra réellement exitwait à la fois après notifyAll car ils devront tous acquérir à nouveau le même moniteur - mais tout aura été averti, dès que le premier quitte le bloc synchronisé, le suivant acquiert le verrou, etc.

8
Jon Skeet