【问题标题】:Java Wait and Notify: IllegalMonitorStateExceptionJava 等待和通知:IllegalMonitorStateException
【发布时间】:2011-10-30 21:23:33
【问题描述】:

我不完全理解waitnotifyObject)是如何工作的,因此我不得不将我的尝试缩减到以下代码部分。

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

目前我在调用Main.main.wait(); 时收到 IllegalMonitorStateException,但我不明白为什么。据我所知,我需要同步Runner.run,但这样做我假设它只会通知一个线程,而我的想法是通知所有线程。

我查看了java.util.concurrent,但找不到合适的替代品(也许我只是遗漏了一些东西)。

【问题讨论】:

  • 你永远不应该使用等待和通知。它们在旧 Java 中是必需的,但现在它们应该只供编写库的人使用。您现在应该使用 java.util.concurrent 中的设施。
  • 同意toto2,即使您添加了同步,此代码也不起作用。如果 main 在所有其他线程到达 wait() 之前调用 notifyAll 会发生什么?

标签: java multithreading concurrency


【解决方案1】:

您同时调用了waitnotifyAll,而没有使用synchronized 块。在这两种情况下,调用线程必须拥有您调用该方法的监视器上的锁。

来自notify 的文档(waitnotifyAll 有类似的文档,但请参阅notify 以获得最完整的描述):

该方法只能由作为该对象监视器所有者的线程调用。线程通过以下三种方式之一成为对象监视器的所有者:

  • 通过执行该对象的同步实例方法。
  • 通过执行在对象上同步的同步语句的主体。
  • 对于 Class 类型的对象,通过执行该类的同步静态方法。

一次只有一个线程可以拥有一个对象的监视器。

只有一个线程能够在notifyAll 之后一次真正退出 wait,因为它们都必须再次获取相同的监视器 - 但所有线程都会收到通知,所以一旦第一个退出同步块,下一个将获取锁等。

【讨论】:

    【解决方案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 for wait() 提到这一点:

    该方法只能由作为该对象监视器所有者的线程调用。请参阅notify 方法,了解线程可以成为监视器所有者的方式。

    抛出:

    IllegalMonitorStateException – 如果当前线程不是该对象监视器的所有者。

    来自notify()

    线程在以下三种情况之一中成为对象监视器的所有者 方式:

    • 通过执行该对象的同步实例方法。
    • 通过执行在对象上同步的synchronized 语句的主体。
    • 对于Class 类型的对象,通过执行该类的同步静态方法。

    【讨论】:

    • wait() 会在阻塞其线程之前释放对象监视器,因此它不会阻塞其他调用 notify 的线程。
    • 对于此处链接的人员,您调用wait() 的对象必须与您正在同步的对象相同。在 synchronized(y) 块中调用 x.wait() 将产生相同的异常。
    猜你喜欢
    • 1970-01-01
    • 2019-12-23
    • 1970-01-01
    • 2010-10-27
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多