Подтвердить что ты не робот

Java Wait и Notify: IllegalMonitorStateException

Я не совсем понимаю, как работают wait и notify (of Object), и в результате я вынужден уменьшить мои попытки в следующем разделе кода.

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!");
  }
}

В настоящее время я получаю исключение IllegalMonitorStateException при вызове Main.main.wait();, но я не понимаю, почему. Из того, что я вижу, мне нужно синхронизировать Runner.run, но при этом я предполагаю, что он будет уведомлять только об одном потоке, когда идея состоит в том, чтобы уведомить их обо всех.

Я посмотрел на java.util.concurrent, но я не могу найти подходящую замену (возможно, я просто что-то пропустил).

4b9b3361

Ответ 1

Вы не можете wait() на объекте, если текущий поток не владеет этим объектом. Для этого вам нужно synchronize:

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

То же правило применяется и к notify()/notifyAll().

Javadocs для wait() упоминает об этом:

Этот метод должен вызываться только потоком, который является владельцем этого монитора объекта. См. Метод notify для описания способов, которыми поток может стать владельцем монитора.

Броски:

IllegalMonitorStateException - если текущий поток не является владельцем этого монитора объекта.

И из notify():

Нить становится владельцем монитора объекта в одном из трех способы:

  • Выполняя синхронизированный метод экземпляра этого объекта.
  • Выполняя тело оператора synchronized, который синхронизируется с объектом.
  • Для объектов типа Class, выполнив синхронизированный статический метод этого класса.

Ответ 2

Вы вызываете как wait, так и notifyAll без использования блока synchronized. В обоих случаях вызывающий поток должен иметь блокировку на мониторе, на который вы вызываете метод.

В документах для notify (wait и notifyAll есть аналогичная документация, но обратитесь к notify для самого полного описания):

Этот метод должен вызываться только потоком, который является владельцем этого монитора объекта. Нить становится владельцем монитора объекта одним из трех способов:

  • Выполняя синхронизированный метод экземпляра этого объекта.
  • Выполняя тело синхронизированного оператора, который синхронизируется с объектом.
  • Для объектов типа Class, выполнив синхронизированный статический метод этого класса.

Только один поток за раз может владеть монитором объекта.

Только один поток сможет фактически выйти из wait за один раз после notifyAll, поскольку все они должны будут снова получить тот же монитор, но все будут уведомлены, поэтому, как только первый выйдет из синхронизированного блока, следующий получит блокировку и т.д.