Why does notifyAll() raise IllegalMonitorStateException when synchronized on Integer?

30,831

Solution 1

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.

Solution 2

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 "").

Solution 3

Incrementing the Integer makes the old foo disappear and be replaced with a brand new object foo which is not synchronized with the previous foo variable.

Here is an implementation of AtomicInteger that erickson suggested above. In this example foo.notifyAll(); does not produce a java.lang.IllegalMonitorStateException beause the AtomicInteger Object is not refreshed when foo.incrementAndGet(); is run.

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);
    }
}

Output:

foo is: 2

Solution 4

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

Share:
30,831
jjvainio
Author by

jjvainio

Updated on May 31, 2020

Comments

  • jjvainio
    jjvainio almost 4 years

    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)
    
  • jjvainio
    jjvainio over 15 years
    I failed to realize that incrementing the Integer would allocate a new object instead of changing the value of the existing object.
  • dmitrii
    dmitrii almost 13 years
    Trying to wait/notify using an Enum can also lead to this condition: synchronized (myEnum) { myEnum=MyEnum.NEW_VALUE; myEnum.notify(); }
  • Oded Breiner
    Oded Breiner over 9 years
    For those looking for a solution to a boolean, use AtomicBoolean as the wrapper type
  • coffee_machine
    coffee_machine over 9 years
    @dmitrii, yes indeed, but here you are explicitly modifying the reference. That applies to ANY object reference, not only enums.
  • vehsakul
    vehsakul over 8 years
    It's very tempting to use a primitive type wrapper both as the synchronizer and the condition data. What a mistake!