【问题标题】:IllegalMonitorStateException on wait() callwait() 调用时出现 IllegalMonitorStateException
【发布时间】:2010-12-04 23:01:39
【问题描述】:

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

【问题讨论】:

  • Thread.wait() 不存在,可能是this.wait()

标签: java multithreading wait


【解决方案1】:

您需要在synchronized 块中才能使Object.wait() 工作。

另外,我建议查看并发包,而不是老式的线程包。他们更安全,更方便easier to work with

编辑

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

【讨论】:

  • 好收获。我以为他的意思是 Object.wait() 并从线程调用
  • 您正在等待的对象上的同步块。是否愿意编辑此答案以使其更加清晰?谢谢。
【解决方案2】:

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 数据结构。

【讨论】:

  • 我从来不明白这是如何工作的,因为等待和通知都在同一个对象(锁)上的同步块中。由于等待线程在块中,那不应该让通知线程阻塞在“同步(锁定)”行吗?
  • @Brent212 对于wait 以外的任何方法,是的,您永远不会到达notify。但是,在Object.wait 的 API 文档中,“线程释放了此监视器的所有权”。因此,虽然在 wait 中,它就好像它在封闭的 synchronized 块之外(对于同一个对象,可能在同一个对象上有多个 synchronized 块)。
【解决方案3】:

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

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

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

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

1> 等待对象的监视器而不拥有指定的监视器。

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

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

同步(对象)

object.wait()

如果两个 object 相同...则不会出现非法MonitorException。

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

【讨论】:

  • 其实这行不通。我试过了。我创建了一个 Runnable,锁定它(使用同步块)并在该块内我在 UI 线程(Android)上运行 Runnable,然后我执行 myRunnable.wait(),我仍然得到异常。
  • 很好的解释!!我在没有指定对象的情况下执行 wait(),因此它采用了实例,并在另一个对象上同步。现在我正在使用 otherObject.wait() 并且它有效!
【解决方案4】:

根据您的 cmets,听起来您正在做这样的事情:

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 实例并让线程调用latch 上的await() 以暂停自身。然后主线程会在闩上调用countDown(),让暂停的线程继续。

  3. 与前面的点正交,使用Thread 对象作为锁/互斥锁可能会导致问题。例如,Thread::join 的 javadoc 说:

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

【讨论】:

    【解决方案5】:

    由于您尚未发布代码,我们有点在黑暗中工作。异常的细节是什么?

    您是从线程内部还是外部调用 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,这就是我试图描述的。
    • 您是在说worker.wait() 行吗?那么你应该在worker上同步,而不是在锁上。
    【解决方案6】:

    为了处理 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();
            }
        }
    }
    

    【讨论】:

      【解决方案7】:

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

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

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

      【讨论】:

        【解决方案8】:

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

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

        事实上,“lock”作为参数在 synchronized() 中被传递,它也被用于“lock”.notifyAll();

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

        【讨论】:

          【解决方案9】:

          我在尝试从不同的 class / 线程唤醒 / 中的线程时收到了 IllegalMonitorStateException。在java 8 中,您可以使用lock features of the new Concurrency API 代替 synchronized 函数。

          我已经在WeakHashMap 中存储了asynchronous websocket 事务的对象。在我的情况下,解决方案也是store a lock object in a ConcurrentHashMap 回复synchronous注意condition.await(不是.wait)。

          为了处理多线程,我使用了Executors.newCachedThreadPool() 创建了thread pool

          【讨论】:

            【解决方案10】:

            使用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
                }
            
            }
            

            【讨论】:

              【解决方案11】:

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

              synchronized(obj)
              {
                 obj.wait()
              }
              

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

              【讨论】:

                猜你喜欢
                • 1970-01-01
                • 2020-05-11
                • 1970-01-01
                • 2022-01-09
                • 1970-01-01
                • 2023-03-13
                • 1970-01-01
                • 2018-11-02
                • 2011-03-21
                相关资源
                最近更新 更多