java.lang.IllegalMonitorStateException: (m=null) 获取监视器失败

40

为什么会发生这种情况?事实上,监视对象肯定不为 null,但我们经常遇到这个异常:

java.lang.IllegalMonitorStateException: (m=null) Failed to get monitor for (tIdx=60)
        at java.lang.Object.wait(Object.java:474)
        at ...

引发此问题的代码是一个简单的池解决方案:

    public Object takeObject() {
        Object obj = internalTakeObject();
        while (obj == null) {
            try {
                available.wait();
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
            obj = internalTakeObject();
        }
        return obj;
    }

    private Object internalTakeObject() {
        Object obj = null;
        synchronized (available) {
            if (available.size() > 0) {
                obj = available.keySet().iterator().next();
                available.remove(obj);
                synchronized (taken) {
                    taken.put(obj, Boolean.valueOf(true));
                }
            }
        }
        return obj;
    }

    public void returnObject(Object obj) {
        synchronized (taken) {
            taken.remove(obj);
        }
        synchronized (available) {
            if (available.size() < size) {
                available.put(obj, Boolean.valueOf(true));
                available.notify();
            }
        }
    }

我是不是漏掉了什么重要的东西?

编辑: 异常出现在 available.wait(); 这一行。


你能告诉我们源代码中第474行是什么吗? - flybywire
异常发生在available.wait()这一行,但474行来自java.lang.Object类。 - Andrey Adamovich
4个回答

77

请参考 Object.wait 的 javadoc。

特别注意:"当前线程必须拥有此对象的监视器。" 和 "[throws] IllegalMonitorStateException - 如果当前线程不是该对象监视器的所有者。" 也就是说,你需要在调用 wait 方法时对该对象进行同步。

因此,你的代码应该如下所示:

synchronized (available) {
    available.wait();
}

6
值得注意的是:如果在 available.notify() 上发生同样的异常,请使用相同的模式。 - Andreas Dolk
我尝试使用同步函数而不是块来完成相同的事情,但它抛出了IllegalMonitorStateException异常。为什么会这样? - Hunter S
Hunter,当您将成员函数同步化时,您正在对对象进行锁定 - 因此this.wait()应该可以工作。您到底做了什么? - tgdavies
1
一个同步方法实际上就是this.wait(),但是那些拥有在执行这些方法的对象的监视器的人,而不是available。因此这与synchronized(available)不同。 - Rhubarb

7

available.wait(); 必须在 synchronized(available) 段中运行


1
你从中得到了 "IllegalMonitorStateException" 错误。
available.wait()

因为调用wait()方法的当前线程不是引用“available”对象监视器的所有者。

要使线程成为对象监视器的所有者,有三种方法。

  1. 通过执行该对象的同步实例方法。
  2. 通过执行同步块的主体,在其中对该对象进行同步。
  3. 对于类型为Class的对象,通过执行该类的同步静态方法。

每种情况的简单示例代码。这三个代码片段是每种类型的独立类,只需复制代码并运行即可。我在代码中添加了大量注释以解释每种情况发生的情况。如果注释太多,请删除它们以使代码更加简洁。

此外,首先阅读main()方法中的代码,以先了解threadOne和threadTwo的情况。

  1. By executing a synchronized instance method of that object.

    import static java.lang.System.out;
    
    public class SynchronizedInstanceMethodClass {
    
        synchronized void synchronizedInstanceMethod() { // threadOne acquire the monitor for "this" and continue.
    
                try {
    
                    out.println("EVENT #1 threadOne is about to strat waiting on the "
                            +"monitor it already has - [\"this\"]....");
    
                    this.wait(); // The threadOne already have the monitor for "this", 
                                //  just release the monitor and go and wait threadOne.
    
                    out.println("EVENT #3 Notify received and continue execution...");
    
                } catch (InterruptedException interruptedException) {
                    interruptedException.printStackTrace();
                }
        }
    
    
        synchronized void notifierForAllThreads() { // threadTwo acquire the monitor for "this", 
                                                   // which was released by threadOne when it went to waiting and contine.
    
                out.println("EVENT #2 threadTwo is about to notify all threads(including threadOne) "
                        +"   waiting on the monitor of -[\"this\"]....");
    
                this.notifyAll(); // threadTwo who owns the monitor on "this" notifies all 
                                 // threads waiting on "this" and releases the monitor
        }
    
        public static void main(String [] args) {
    
            SynchronizedInstanceMethodClass mc  = new SynchronizedInstanceMethodClass();
            Thread threadOne = new Thread(() -> {mc.synchronizedInstanceMethod();});
            Thread threadTwo = new Thread(() -> {mc.notifierForAllThreads();});
    
            threadOne.start(); // Start the waiting of Thread one
            threadTwo.start(); // Notify the waiting threadOne
        }
    
    }
    
  2. By executing the body of a synchronized block that synchronizes on the object.

    import static java.lang.System.out;
    
    public class SynchronizedBlockClass {
    
        void synchronizedBlockInstanceMethod() {
    
            synchronized (this) { // threadOne acquire the monitor for "this" and continue.
    
                try {
    
                    out.println("EVENT #1 threadOne is about to strat waiting on the "
                                +"monitor it already has - [\"this\"]....");
    
                    this.wait(); // The threadOne already have the monitor for "this", 
                                //  just release the monitor and go and wait threadOne.
    
                    out.println("EVENT #3 Notify received and continue execution...");
                } catch (InterruptedException interruptedException) {
                    interruptedException.printStackTrace();
                }
            }
        }
    
    
        void synchronizedBlockNotifierForAllThreads() {
    
            synchronized (this) { // threadTwo acquire the monitor for "this", 
                                 // which was released by threadOne when it went to waiting and continue.
    
                    out.println("EVENT #2 threadTwo is about to notify all threads(including threadOne) "
                            +"   waiting on the monitor of -[\"this\"]....");
    
                    this.notifyAll(); // threadTwo who owns the monitor on "this" notifies all 
                                     // threads waiting on "this" and releases the monitor
                }
        }
    
        public static void main(String [] args) {
            SynchronizedBlockClass mc  = new SynchronizedBlockClass();
            Thread threadOne = new Thread(() -> {mc.synchronizedBlockInstanceMethod();});
            Thread threadTwo = new Thread(() -> {mc.synchronizedBlockNotifierForAllThreads();});
    
            threadOne.start(); // Start the waiting of Thread one
            threadTwo.start(); // Notify the waiting threadOne
        }
    
    }
    
  3. For objects of type Class by executing a synchronized static method of that class.

    import static java.lang.System.out;
    
    public class StaticClassReferenceClass {
    
        void synchronizedBlockInstanceMethod() {
    
            synchronized (StaticClassReferenceClass.class) { // threadOne acquire the monitor for class literal and continue.
    
                try {
    
                    out.println("EVENT #1 threadOne is about to strat waiting on the "
                                +"monitor it already has - [StaticClassReferenceClass.class]....");
    
                    StaticClassReferenceClass.class.wait(); // The threadOne already have the monitor for the class literal, 
                                //  So it just release the monitor and go and wait.
    
                    out.println("EVENT #3 Notify received and continue execution...");
                } catch (InterruptedException interruptedException) {
                    interruptedException.printStackTrace();
                }
            }
        }
    
    
        void synchronizedBlockNotifierForAllThreads() {
    
            synchronized (StaticClassReferenceClass.class) { // threadTwo acquire the monitor for the class literal, 
                                 // which was released by threadOne when it went to waiting.
    
                    out.println("EVENT #2 threadTwo is about to notify all threads(including threadOne) "
                            +"   waiting on the monitor of -[StaticClassReferenceClass.class]....");
    
                    StaticClassReferenceClass.class.notifyAll(); // threadTwo who owns the monitor on the class literal notifies all 
                                     // threads waiting on it and releases the monitor
                }
        }
    
        public static void main(String [] args) {
            StaticClassReferenceClass mc  = new StaticClassReferenceClass();
            Thread threadOne = new Thread(() -> {mc.synchronizedBlockInstanceMethod();});
            Thread threadTwo = new Thread(() -> {mc.synchronizedBlockNotifierForAllThreads();});
    
            threadOne.start(); // Start the waiting of Thread one
            threadTwo.start(); // Notify the waiting threadOne
        }
    
    }
    

0

takeObject() 方法必须同步,或者我们必须在该方法内部编写同步块。我希望您能在编译时捕获异常。


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