Ожидание и уведомление Java: IllegalMonitorStateException - PullRequest
35 голосов
/ 19 августа 2011

Я не совсем понимаю, как работают wait и notify (из 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, но я не могу найти подходящую замену (возможно, я что-то упускаю).

Ответы [ 2 ]

60 голосов
/ 19 августа 2011

Вы не можете 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 путем выполнения синхронизированного статического метода этого класса.
7 голосов
/ 19 августа 2011

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

Из документов для notify (wait и notifyAll имеют аналогичную документацию, но полное описание приведено в notify):

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

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

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

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...