41

为什么这个测试程序会导致java.lang.IllegalMonitorStateException?

public class test {
    static Integer foo = new Integer(1);
    public static void main(String[] args) {
        synchronized(foo) {
            foo++;
            foo.notifyAll();
        }
        System.err.println("Success");
    }
}

结果:

Exception in thread "main" java.lang.IllegalMonitorStateException
        at java.lang.Object.notifyAll(Native Method)
        at test.main(test.java:6)
4

4 回答 4

58

您已经正确地注意到notifyAll必须从同步块中调用。

但是,在您的情况下,由于自动装箱,您同步的对象与您调用的实例notifyAll不同。事实上,新的、递增的foo实例仍然被限制在堆栈中,并且不可能在wait调用时阻塞其他线程。

您可以实现自己的可变计数器,在该计数器上执行同步。根据您的应用程序,您可能还会发现AtomicInteger满足您的需求。

于 2008-11-03T23:37:04.700 回答
3

您还应该警惕锁定或通知可以由 JVM 实习的对象(如 String 和 Integer)(以防止创建大量表示整数 1 或字符串“”的对象)。

于 2008-11-03T23:47:41.807 回答
3

递增 Integer 会使旧的 foo 消失,并被一个与前一个 foo 变量不同步的全新对象 foo 替换。

这是埃里克森在上面建议的 AtomicInteger 的实现。在这个例子中 foo.notifyAll(); 不会产生 java.lang.IllegalMonitorStateException 因为 AtomicInteger 对象在 foo.incrementAndGet() 时没有刷新;正在运行。

import java.util.concurrent.atomic.AtomicInteger;

public class SynchronizeOnAPrimitive {
    static AtomicInteger foo = new AtomicInteger(1);
    public static void main(String[] args) {
        synchronized (foo) {
            foo.incrementAndGet();
            foo.notifyAll();
        }
        System.out.println("foo is: " + foo);
    }
}

输出:

foo is: 2
于 2011-03-18T21:28:03.613 回答
1

正如埃里克森所指出的,没有后增量运算符的代码可以正常工作:

static Integer foo = new Integer(1);

public static void main(String[] args) {
    synchronized (foo) {
        foo.notifyAll();
    }
    System.out.println("Success");
}

输出:

成功

于 2008-11-03T23:43:57.243 回答