【问题标题】:Why does this throw an IllegalMonitorStateException?为什么这会引发 IllegalMonitorStateException?
【发布时间】:2013-03-23 19:23:32
【问题描述】:

我正在研究 Java 多线程。我对 C/C++ pthreads 非常熟悉,但在使用 Java notify()wait() 函数时遇到了问题。

我知道只有在不“拥有”(又名未同步)的线程调用通知/等待时才会抛出 IllegalMoinitorStateException

在编写我的应用程序时,我遇到了这个问题。我用以下测试代码隔离了问题:

public class HelloWorld
{
    public static Integer notifier = 0;
    public static void main(String[] args){
        notifier = 100;
        Thread thread = new Thread(new Runnable(){
            public void run(){
                    synchronized (notifier){
                            System.out.println("Notifier is: " + notifier + " waiting");
                            try{
                                notifier.wait();
                                System.out.println("Awake, notifier is " + notifier);
                            }
                            catch (InterruptedException e){e.printStackTrace();}
                    }
            }});
        thread.start();
        try{
                Thread.sleep(1000);
            }
        catch (InterruptedException e){
                e.printStackTrace();
            }
        synchronized (notifier){
            notifier = 50;
            System.out.println("Notifier is: " + notifier + " notifying");
            notifier.notify();
        }
        }
    }

这个输出:

    Exception in thread "main" java.lang.IllegalMonitorStateException
        at java.lang.Object.notify(Native Method)
        at HelloWorld.main(HelloWorld.java:27)

我相信我已经获得了通知对象的锁定。我做错了什么?

谢谢!

编辑:

从这个可能的重复项(Synchronizing on an Integer value)看来,在 Integer 上同步似乎不是一个好主意,因为很难确保您在同一个实例上同步。由于我正在同步的整数是全局可见静态整数,为什么我会得到不同的实例?

【问题讨论】:

  • 只是另一个提示,因为它已经被回答了:尝试将通知器设置为最终。它不会编译,因为您为通知程序分配了不同的值(对象)。

标签: java multithreading


【解决方案1】:

由于notifier = 50;,您在另一个对象上调用notifier.notify();

【讨论】:

    【解决方案2】:

    最初,当您在非主线程中对notifier 调用同步时,它引用的是堆上的对象,其内容为0,因此线程拥有该对象。现在,在使用notifier.wait 将非主线程放在wait 上之后,控制权就交给了主线程。在获得对包含值 0 的 Integer 对象的锁定后,您使 notifier 指向堆内存上包含值 50 的另一个对象,因为 notifier = 50; 实际上等同于 notifier = new Integer(50),这意味着 Integer 的新对象已创建并将其引用传递给notifier。现在,当线程看到notifier.notify 时,看起来主线程现在不再拥有它之前获得的原始对象。所以IllegalMonitorStateException 正在投掷。

    【讨论】:

      【解决方案3】:

      只是为了添加更多信息,您永远不应该在非最终对象上同步。

      您需要在一个常量对象上进行同步。如果您在您分配的任何对象上同步(即更改对象),则该对象不是恒定的,一旦它分配了该对象并尝试通知它,您将获得IllegalMonitorStateException。这也意味着由于它们在不同的对象上同步,多个线程将同时进入受保护的块,并且会发生竞争条件。

      既然我正在同步的整数是一个全局可见的静态整数,为什么我会得到不同的实例?

      当您为Integer 赋值时,它会将其更改为不同 对象引用——即使它是static。您没有更改同一个对象,因为Integer 不能被变异。所以notifier = 50; 将其分配给与notifier = 0; 不同的对象。

      如果你想使用,例如,一个常量对象,你可以使用AtomicBoolean

        public static final AtomicInteger notifier = new AtomicInteger(0);
        ...
        synchronize (notifier) {
            notifier.set(50);
            ...
        }
      

      在这里,AtomicInteger 可以标记为final,因为它始终是同一个对象。

      欲了解更多信息,请参阅:Why is it not a good practice to synchronize on Boolean?

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2014-07-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多