Austin
Austin

Reputation: 85

Why does this throw an IllegalMonitorStateException?

I am getting into Java multithreading. I am very familiar with C/C++ pthreads, but am having issues with the Java notify() and wait() functions.

I understand that an IllegalMoinitorStateException is only thrown when a thread that doesnt "own" (aka hasnt synchronized) calls notify/wait.

When writing my application, I ran into this issue. I isolated the problem with the following test code:

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

This outputs:

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

I believe I have already acquired the lock on the notifier object. What am I doing wrong?

Thanks!

EDIT:

From this possible duplicate(Synchronizing on an Integer value), it seems that it is not a good idea to synchronize on an Integer because it is hard to make sure you are synchronizing on the same instance. Since my integer I am synchronizing on is a global visibile static integer, why am I getting different instances?

Upvotes: 2

Views: 373

Answers (3)

Gray
Gray

Reputation: 116848

Just to add more information, you should never synchronize on a non-final object.

You need to synchronize on a constant object. If you synchronized on any object that you are assigning (i.e. changing the object) then the object is not constant and once it assigns the object and tries to notify on it, you will get the IllegalMonitorStateException. It also means that because they are synchronizing on different objects, multiple threads will be entering the protected block at the same time and race conditions will happen.

Since my integer I am synchronizing on is a global visibile static integer, why am I getting different instances?

When you assign a value to an Integer it changes it to a different object reference -- even if it is static. You are not changing the same object because Integer can't be mutated. So notifier = 50; assigns it to a different object than notifier = 0;.

If you want to use, for example, a constant object you can use an AtomicBoolean:

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

Here, AtomicInteger can be marked final because it's the same object all of the time.

For more information, see: Why is it not a good practice to synchronize on Boolean?

Upvotes: 0

Vishal K
Vishal K

Reputation: 13066

Initially when you calling synchronized on notifier in non-main Thread it was referencing to the object on heap whose content was 0 hence the thread owns that object . Now after non-main thread is put on wait using notifier.wait the control is given to the main thread . There after acquiring lock on object of Integer containing value 0 you made the notifier to refer the other object on heap memory containing value 50 because notifier = 50; is actually equivalent to notifier = new Integer(50) which means a new object of Integer is created and its reference is passed to notifier. Now , when the thread sees notifier.notify it looks that the Main thread now no longer owns the original object that it had acquired before. So IllegalMonitorStateException is throwing.

Upvotes: 0

UmNyobe
UmNyobe

Reputation: 22890

Because of notifier = 50; you are calling notifier.notify(); on a different object.

Upvotes: 5

Related Questions