168

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

4

11 回答 11

184

你需要在一个synchronized街区才能Object.wait()工作。

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

编辑

我假设您的意思Object.wait()是您的例外是当您尝试在不持有对象锁定的情况下获得访问权限时发生的情况。

于 2009-10-08T11:11:53.913 回答
57

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.atomicjava.util.concurrent尽可能使用数据结构。

于 2009-10-08T12:06:07.137 回答
26

我知道这个帖子已经快 2 年了,但仍然需要关闭这个帖子,因为我也遇到了同样的问题。

请反复阅读此非法监视器异常的定义...

抛出 IllegalMonitorException 以指示线程已尝试在对象的监视器上等待或通知在对象的监视器上等待但不拥有指定监视器的其他线程。

这一行一再表示,当两种情况之一发生时,IllegalMonitorException 就会出现......

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

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

有些人可能已经得到了他们的答案......谁都没有,那么请检查2个陈述......

同步(对象)

对象.wait()

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

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

于 2012-01-25T10:35:56.157 回答
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对象作为锁/互斥锁可能会导致问题。例如,javadocThread::join说:

    此实现使用以 为this.wait条件的调用循环this.isAlive。当一个线程终止时,该this.notifyAll方法被调用。建议应用程序不要在实例上使用waitnotify或。notifyAllThread

于 2009-10-08T15:21:00.550 回答
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) { }

    }
于 2009-10-08T11:29:13.480 回答
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();
        }
    }
}
于 2018-10-06T10:35:50.040 回答
0

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

如何让线程等到它收到通知?

您只能让当前线程等待。任何其他线程只能轻轻地要求等待,如果它同意的话。
如果你想等待某些条件,你需要一个锁对象——Thread.class 对象是一个非常糟糕的选择——它是一个单例 AFAIK,所以在它上面同步(除了 Thread 静态方法)是危险的。
Tom Hawtin 已经解释了同步和等待的细节。 java.lang.IllegalMonitorStateException表示您正在尝试等待未同步的对象 - 这样做是非法的。

于 2009-10-08T14:12:07.473 回答
0

不确定这是否会帮助其他人,但这是解决我在用户“Tom Hawtin - tacklin”上面的回答中的问题的关键部分:

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

只是“lock”作为参数在 synchronized() 中传递,它也用于“lock”.notifyAll();

一旦我在这两个地方成功了,我就开始工作了

于 2014-10-10T04:25:54.780 回答
0

我收到了一段IllegalMonitorStateException时间试图从不同的/线程中唤醒一个class线程。java 8您可以使用lock新的并发 API 的功能而不是synchronized函数。

我已经将asynchronouswebsocket 事务的对象存储在WeakHashMap. 在我的情况下,解决方案是将对象也存储lockConcurrentHashMap在 a 中以供synchronous回复。注意condition.await不是.wait)。

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

于 2018-01-19T10:29:01.057 回答
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
    }

}
于 2018-01-20T11:23:27.573 回答
0

要在对象上调用 wait()/notify(),它需要在同步块内。所以首先你必须锁定对象然后才能调用这些函数。

synchronized(obj)
{
   obj.wait()
}

详细解释: https ://dzone.com/articles/multithreading-java-and-interviewspart-2

于 2021-09-29T14:15:15.030 回答