Ожидание и уведомление Java: IllegalMonitorStateException

Я не совсем понимаю как 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 ответа

Решение

Ты не можешь 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, выполняя синхронизированный статический метод этого класса.

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

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

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

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

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

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

Другие вопросы по тегам