Reputation: 85
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
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
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
Reputation: 22890
Because of notifier = 50;
you are calling notifier.notify();
on a different object.
Upvotes: 5