在wait()调用上发生IllegalMonitorStateException


161

我在Java中为程序使用多线程。我已经成功运行了线程,但是当我使用线程时Thread.wait(),它正在抛出java.lang.IllegalMonitorStateException。如何让线程等待通知它?


2
Thread.wait()不存在,可能是this.wait()
2015年

Answers:


174

您需要synchronized先行一步Object.wait()才能工作。

另外,我建议您查看并发程序包,而不是旧式的线程程序包。它们更安全,更易于使用

快乐的编码。

编辑

我以为您的意思Object.wait()是,当您尝试在不持有对象锁定的情况下获取访问权限时会发生异常。


1
接得好。我
以为

2
您正在等待的对象上的同步块。希望编辑此答案以使其更加清楚吗?谢谢。
2013年

55

wait是在中定义的Object,不是Thread。显示器打开Thread有点不可预测。

尽管所有Java对象都有监视器,但是通常最好使用专用锁:

private final Object lock = new Object();

通过使用命名类,您可以以较小的内存开销(每个进程大约2K)来更轻松地读取诊断信息:

private static final class Lock { }
private final Object lock = new Lock();

为了waitnotify/ notifyAll一个对象,您需要用synchronized语句持有该锁。另外,您将需要一个while循环来检查唤醒条件(在线程上找到一个好的文字以解释原因)。

synchronized (lock) {
    while (!isWakeupNeeded()) {
        lock.wait();
    }
}

通知:

synchronized (lock) {
    makeWakeupNeeded();
    lock.notifyAll();
}

进入多线程时,同时了解Java语言和java.util.concurrent.locks锁(和java.util.concurrent.atomic)非常值得。但是,请java.util.concurrent尽可能使用数据结构。


5
鉴于wait和notify都在同一个对象(锁)上的同步块中,所以我从未理解过它是如何工作的。由于等待线程在该块中,因此不应该将通知线程块放在“同步(锁定)”行上吗?
布伦特212年

6
@ Brent212除了其他方法wait,是的,您永远也不会notify。但是,在API文档中Object.wait,“线程释放了此监视器的所有权”。因此,虽然wait它好像在封闭的synchronized块之外(对于同一对象,可能synchronized在同一对象上是多个块)。
Tom Hawtin-大头钉

24

我知道这个主题已经使用了将近2年了,但是由于我也有同样的问题参加了这个Q / A会议,所以仍然需要关闭它。

请一遍又一遍地阅读对invalidMonitorException的定义...

抛出IllegalMonitorException,以指示线程试图在对象监视器上等待,或者通知其他线程在对象监视器上等待而没有拥有指定的监视器。

这行反复说,当出现两种情况之一时,IllegalMonitorException出现。

1>在不拥有指定监视器的情况下等待对象的监视器。

2>通知其他在对象监视器上等待的线程,而不拥有指定的监视器。

有些人可能会得到答案...谁都不知道,请检查2条语句...。

同步的(对象)

object.wait()

如果两个对象都相同...则不会出现非法监视器异常。

现在再次阅读IllegalMonitorException定义,您将不会再忘记它...


实际上,那是行不通的。我试过了 我创建了一个Runnable,对其进行了锁定(使用同步块),并在该块内在UI线程(Android)上运行Runnable,然后执行myRunnable.wait(),但仍然出现异常。
Ted

出色的解释!我在做wait()时没有指定对象,所以它接受了实例,并与另一个对象同步。现在我正在使用otherObject.wait(),它可以工作了!
费斯卡

6

根据您的评论,听起来您正在执行以下操作:

Thread thread = new Thread(new Runnable(){
    public void run() { // do stuff }});

thread.start();
...
thread.wait();

有三个问题。

  1. 正如其他人所说,obj.wait()只有在当前线程持有的原始锁/互斥锁的情况下才能调用obj。如果当前线程不持有该锁,则您将看到异常。

  2. thread.wait()呼叫没有执行您似乎期望的操作。具体来说,thread.wait() 不会导致指定线程等待。而是使当前线程等待,直到其他线程调用thread.notify()thread.notifyAll()

    实际上,没有安全的方法可以强制Thread实例在不希望的情况下暂停。(Java对此最接近的Thread.suspend()方法是已弃用的方法,但是该方法本质上是不安全的,如Javadoc中所述。)

    如果你想在新开工Thread暂停,做到这一点的最好办法是创建一个CountdownLatch实例,并有线程调用await()上的闩锁暂停本身。然后,主线程将调用countDown()闩锁,以使暂停的线程继续。

  3. 与之前的要点正交,将Thread对象用作锁定/互斥锁可能会导致问题。例如,javadoc Thread::join表示:

    此实现使用this.wait条件为的调用循环this.isAlive。当线程终止时,this.notifyAll将调用该方法。建议应用程序无法使用waitnotifynotifyAllThread实例。


2

由于您尚未发布代码,因此我们在暗中进行工作。例外的详细信息是什么?

您是从线程内部还是外部调用Thread.wait()?

我问这个问题是因为根据IllegalMonitorStateException的javadoc,它是:

抛出该异常指示线程试图在对象的监视器上等待,或者通知其他线程在对象监视器上等待而没有拥有指定的监视器。

为了澄清这个答案,尽管在同步块中被调用,但此等待线程的调用也会引发IllegalMonitorStateException:


     private static final class Lock { }
     private final Object lock = new Lock();

    @Test
    public void testRun() {
        ThreadWorker worker = new ThreadWorker();
        System.out.println ("Starting worker");
        worker.start();
        System.out.println ("Worker started - telling it to wait");
        try {
            synchronized (lock) {
                worker.wait();
            }
        } catch (InterruptedException e1) {
            String msg = "InterruptedException: [" + e1.getLocalizedMessage() + "]";
            System.out.println (msg);
            e1.printStackTrace();
            System.out.flush();
        }
        System.out.println ("Worker done waiting, we're now waiting for it by joining");
        try {
            worker.join();
        } catch (InterruptedException ex) { }

    }

@CPerkins:我认为您混淆了执行线程和作为对象的对象wait()
罗伯特·蒙提亚努

@Robert-也许我是,但我不这么认为。如果您启动一个Thread实例,然后要求它等待,您将得到一个IllegalMonitorStateException,这就是我试图描述的。
CPerkins,2009年

您在谈论worker.wait()电话吗?然后,您应该在辅助服务器上同步,而不是在锁上同步。
罗伯特·蒙提亚努

1

为了处理IllegalMonitorStateException,必须验证只有在调用线程拥有适当的monitor的情况下,wait,notify和notifyAll方法的所有调用才发生。最简单的解决方案是将这些调用包含在同步块中。在synced语句中将调用的同步对象是必须获取其监视器的对象。

这是了解显示器概念的简单示例

public class SimpleMonitorState {

    public static void main(String args[]) throws InterruptedException {

        SimpleMonitorState t = new SimpleMonitorState();
        SimpleRunnable m = new SimpleRunnable(t);
        Thread t1 = new Thread(m);
        t1.start();
        t.call();

    }

    public void call() throws InterruptedException {
        synchronized (this) {
            wait();
            System.out.println("Single by Threads ");
        }
    }

}

class SimpleRunnable implements Runnable {

    SimpleMonitorState t;

    SimpleRunnable(SimpleMonitorState t) {
        this.t = t;
    }

    @Override
    public void run() {

        try {
            // Sleep
            Thread.sleep(10000);
            synchronized (this.t) {
                this.t.notify();
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

0

在与Thread.class对象同步的代码中,Thread.wait()调用是有意义的。我认为这不是你的意思。
你问

如何使线程等待通知它?

您只能使当前线程等待。如果同意,只能轻轻地要求任何其他线程等待。
如果要等待某种条件,则需要一个锁定对象-Thread.class对象是一个非常糟糕的选择-它是单例AFAIK,因此在其上进行同步(Thread静态方法除外)很危险。
Tom Hawtin已解释了同步和等待的详细信息。 java.lang.IllegalMonitorStateException表示您正在尝试等待未同步的对象-这样做是非法的。


0

不知道这是否会帮助别人,但这是解决用户“ Tom Hawtin-tacklin”的上述问题的关键部分:

synchronized (lock) {
    makeWakeupNeeded();
    lock.notifyAll();
}

只是事实是,“ lock”作为参数传递给了synced(),并且它也用在了“ lock”中。

一旦在这两个地方做到了,我就能正常工作


0

我收到了一段IllegalMonitorStateException时间,试图从另一个线程中唤醒一个class线程。在中,java 8您可以使用lock新的并发API 的功能代替synchronized功能。

我已经在将asynchronousWebsocket事务存储对象WeakHashMap。在我的情况下,解决方案是还将对象存储lockConcurrentHashMap for synchronous答复中。需要注意condition.await(不.wait)。

为了处理多线程,我使用了一个Executors.newCachedThreadPool()创建线程池


0

那些使用Java 7.0或更低版本的人可以参考我在这里使用的代码,它可以正常工作。

public class WaitTest {

    private final Lock lock = new ReentrantLock();
    private final Condition condition = lock.newCondition();

    public void waitHere(long waitTime) {
        System.out.println("wait started...");
        lock.lock();
        try {
            condition.await(waitTime, TimeUnit.SECONDS);
        } catch (InterruptedException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        lock.unlock();
        System.out.println("wait ends here...");
    }

    public static void main(String[] args) {
        //Your Code
        new WaitTest().waitHere(10);
        //Your Code
    }

}
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.