当前位置: 首页 > 知识库问答 >
问题:

Java等待并通知:IllegalMonitorStateException

柳志专
2023-03-14

我不完全理解waitnotify(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!");
  }
}

当前,我在调用main.main.wait();时得到一个非法MonitorStateException,但我不明白为什么。从我所看到的情况来看,我需要同步runner.run,但在这样做的时候,我假设它只会通知一个线程,而我的想法是通知所有线程。

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

共有2个答案

蒋硕
2023-03-14

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

notify的文档(waitnotifyall具有类似的文档,但有关最完整的说明,请参阅notify):

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

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

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

notifyall之后,一次只有一个线程能够实际退出wait,因为它们都必须再次获取相同的监视器--但所有线程都已被通知,因此一旦第一个线程退出同步块,下一个线程将获取锁等。

淳于健
2023-03-14

除非当前线程拥有对象的监视器,否则不能在对象上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()

wait()的Javadocs提到:

此方法只应由作为此对象的监视器所有者的线程调用。有关线程可以成为监视器所有者的方式的说明,请参阅notify方法

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

并从notify():

线程通过以下三种方式之一成为对象监视器的所有者:

  • 通过执行该对象的同步实例方法。
  • 通过执行对对象进行同步的synchronized语句的主体。
  • 对于class类型的对象,通过执行该类的同步静态方法。
 类似资料:
  • 问题内容: 我正在将程序从Java转移到Objective C,需要使用在Java中经常用于线程化的wait和notify方法,但似乎在Objective C中找不到任何等效的方法。我尝试使用NSLock对象,但是我没有认为它没有用。(我正在使用[NSLock锁定]进行等待,并使用[NSLock解锁]进行通知)我无法找到的Objective C中有什么等效项吗? 问题答案: 您可以使用多种技术。您

  • 我想做一个小练习来习惯等待/通知。我想做的是简单地启动一个线程,然后用等待让它进入睡眠状态,用通知唤醒它,多次。 我的代码是: 我希望这会是这样 相反,这样做: 所以。。。通知似乎没有唤醒打印机线程? 这不应该是一个死锁,因为通过等待,我释放了所有的锁,所以主服务器不应该有任何对打印机的锁,打印机应该能够唤醒并打印。 我做错了什么?

  • 问题内容: 我知道您可以使用lock在c#中锁定对象,但是可以放弃该锁并等待其他东西来通知您它已更改,就像您可以在Java中使用wait和notify一样吗? 在我看来,分别在Java和C#中同步和锁定是同义的。 问题答案: 等效功能(包括常规锁定)在Monitor类中。 C#中的语句等效于调用并带有适当的try / finally块。 有关更多详细信息,请参见我的线程教程或Joe Albahar

  • 问题内容: 我写了一个永远不会停止的测试应用程序。它发出(是一个对象),但是我从不打电话通知。为什么此代码结束?尽管主线程在上同步,但生成的线程仍在运行,因此不会锁定该对象。 结果是主线程等待5秒钟,在此期间工作人员提供其输出。然后,在5秒钟后,程序退出。不等。如果主线程在5秒钟内没有进入睡眠状态(对此行进行了注释),则实际上将等到工作人员完成操作。当然,这里是一种使用方法,但是,出乎意料的是,它

  • 我有一段代码 如您所见,我首先将标志设置为false,这样其中一个线程就可以进入Sum2Elements方法并将其更改为true,从而让所有人都等待。 我知道在同步代码中,只有一个线程可以完成它的任务,这里我有两个同步方法,这是否意味着两个线程在每次通知之后都在尝试执行这个方法? 如果是这样,那么一个线程是否不可能输入Sum2Elements,在另一个线程进入InsertElement之前将标志更

  • 问题内容: 与传统的等待通知机制相比,使用Condition接口/实现的优点是什么?在这里,我引用道格·李(Doug Lea)的评论: 条件将对象监视方法(wait,notify和notifyAll)分解为不同的对象,从而通过与任意Lock实现结合使用,从而使每个对象具有多个等待集。如果Lock替换了同步方法和语句的使用,而Condition替换了Object监视器方法的使用。 我看到这是实现等待