wait() 调用导致 IllegalMonitorStateException 异常

171

我正在使用Java中的多线程来编写程序。

我已经成功地运行了线程,但是当我使用Thread.wait()时,它会抛出java.lang.IllegalMonitorStateException异常。我该如何让线程等待直到被通知?


3
Thread.wait() 方法不存在,可能应该使用 this.wait() 方法。 - Premraj
12个回答

188
你需要在想要等待的对象的一个同步代码块内,才能使Object.wait()函数发挥作用。
此外,我建议使用并发包而不是旧的线程包。它们更安全,也更容易使用
我认为你的意思是当你尝试在没有持有对象锁的情况下获得访问权限时会出现异常,因为你提到了Object.wait() 函数。

1
好的发现。我假设他指的是Object.wait()并从线程中调用。 - reccles
2
在你等待的对象上使用同步块。能否编辑此答案以使其更加清晰?谢谢。 - Gray

57

wait 方法定义在 Object 类中,而不是 ThreadThread 上的监视器有些不可预测。

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

private final Object lock = new Object();

你可以使用一个命名类来获取稍微更易读的诊断信息,但这会以一点内存成本(大约每个进程2K)为代价。

private static final class Lock { }
private final Object lock = new Lock();
为了 wait 或者 notify/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都在同一个对象(锁)的同步块中,我一直不理解它是如何工作的。由于wait线程在块中,这难道不会使notify线程在“synchronized(lock)”行上阻塞吗? - Brent212
6
对于除了wait之外的任何方法,确实永远不会到达notify。然而,在Object.wait的API文档中,“线程释放此监视器的所有权”。因此,在wait期间,就好像它在封闭的synchronized块之外(对于同一对象可能有多个synchronized块)。 - Tom Hawtin - tackline

26

我知道这个帖子已经将近两年了,但仍需要关闭它,因为我也遇到了同样的问题...

请反复阅读IllegalMonitorException的定义...

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

这条语句再次说明,当出现以下两种情况之一时,就会产生IllegalMonitorException....

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

2> 在没有拥有指定监视器的情况下通知等待对象的监视器的其他线程。

一些人可能已经得到了他们的答案...对于那些还没有得到答案的人,请检查以下两个语句....

synchronized (object)

object.wait()

如果两个object是相同的...那么不会出现illegalMonitorException。

现在再次阅读IllegalMonitorException的定义,你就不会再忘记它了...


实际上,那样做行不通。我已经尝试过了。我创建了一个Runnable,在它上面加锁(使用synchronized块),在该块内我在UI线程(Android)上运行Runnable,之后我执行myRunnable.wait(),但仍然会出现异常。 - Ted
非常好的解释!我之前在调用wait()方法时,没有指定对象,结果它占用了实例,并在另一个对象上进行同步。现在我使用otherObject.wait()方法,一切正常! - Fersca

6
根据您的评论,听起来您正在做类似这样的事情:
Thread thread = new Thread(new Runnable(){
    public void run() { // do stuff }});

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

有三个问题。
1.正如其他人所说,如果当前线程没有持有obj的基本锁/互斥锁,则无法调用obj.wait()。如果当前线程没有持有锁,则会出现您看到的异常。
2.thread.wait()调用并不像您期望的那样工作。具体来说,thread.wait() 不会导致指定的线程等待。相反,它使得当前线程等待,直到其他线程调用thread.notify()thread.notifyAll()
实际上,没有安全的方法可以强制暂停一个不想暂停的Thread实例。(Java最接近此功能的方法是已弃用的Thread.suspend()方法,但该方法本质上是不安全的,如Javadoc中所解释的那样。)
如果要让新启动的Thread暂停,最好的方法是创建一个CountdownLatch实例,并让线程调用await()在闩锁上暂停自己。然后,主线程将在闩锁上调用countDown(),以让暂停的线程继续执行。
3.与前面的问题无关,使用Thread对象作为锁/互斥锁可能会导致问题。例如,Thread::join的javadoc说:

此实现使用一系列基于this.isAlive条件的this.wait调用。当一个线程终止时,会调用this.notifyAll方法。建议应用程序不要在Thread实例上使用waitnotifynotifyAll


2
为了处理IllegalMonitorStateException,您必须验证所有wait、notify和notifyAll方法的调用仅在调用线程拥有适当监视器时才发生。最简单的解决方案是将这些调用封装在同步块中。在同步语句中应调用的同步对象是必须获取其监视器的对象。
下面是一个简单的例子,以便更好地理解监视器的概念。
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();
        }
    }
}

2
由于您没有发布代码,我们有些不确定。请问异常的详细信息是什么?
您是在线程内部还是外部调用Thread.wait()呢?我问这个问题是因为根据Java文档中IllegalMonitorStateException的说明,它表示: 抛出以指示线程试图等待对象的监视器或通知等待对象的其他线程而没有拥有指定的监视器。 为了澄清这个答案,即使在同步块内部调用线程的wait方法也会抛出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 Munteanu
@Robert - 也许我是错的,但我不这么认为。如果你启动一个线程实例,然后要求它等待,你会得到一个IllegalMonitorStateException异常,这就是我试图描述的内容。 - CPerkins
你是在谈论 worker.wait() 这一行吗?那么你应该在 worker 上进行同步,而不是在锁上进行同步。 - Robert Munteanu

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
    }

}

0
不确定这是否会对其他人有所帮助,但以下是解决我的问题的关键部分,来自用户“Tom Hawtin - tacklin”在上面的答案中:
synchronized (lock) {
    makeWakeupNeeded();
    lock.notifyAll();
}

在synchronized()中,"lock"作为参数传递,并且在"lock".notifyAll();中也使用了它,这一事实非常重要。

一旦我在这两个地方都做好了,它就可以正常工作了。


0

wait()、notify()和notifyAll()方法应该只在同步上下文中调用。

例如,在同步块中:

syncronized (obj) {
        obj.wait();
}

或者,在一个同步方法中:

syncronized static void myMethod() {
        wait();
}

0

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接