views:

2225

answers:

3

Why does this test program result in a 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");
    }
}

Result:

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

You have noted correctly that notifyAll must be called from a synchronized block.

However, in your case, because of auto-boxing, the object you synchronized on is not the same instance that you invoked notifyAll on. In fact, the new, incremented foo instance is still confined to the stack, and no other threads could possibly be blocked on a wait call.

You could implement your own, mutable counter on which synchronization is performed. Depending on your application, you might also find that AtomicInteger meets your needs.

erickson
I failed to realize that incrementing the Integer would allocate a new object instead of changing the value of the existing object.
jjvainio
This is one of the many gotcha's of Autoboxing/unboxing.
James Schek
A: 

As erickson has noted, the code without the postincrement operator works without error:

static Integer foo = new Integer(1);

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

output:

Success

matt b
+3  A: 

You should also be leery of locking or notifying on objects like String and Integer that can be interned by the JVM (to prevent creating a lot of objects that represent the integer 1 or the string "").

dbt