I\'m trying to have a Thread2 wait for a String and Thread1 to notify on String update, I do synchronize The String object as code below shows, But I still get IllegalMoni
Your code contains a data race because it accesses the mutable string
variable outside of a synchronized
block. Specifically, this happens on the line synchronized(string)
. While dereferencing string
to reach the object whose monitor will be locked, the thread does not already hold a lock on that object. Therefore you have no guarantee which object it will acquire a lock on.
The fact that you mutate the string
variable means that it now points to some other object. When the next thread acquires a lock on that new object, it will not benefit from any happens-before relationship because it is the first thread to ever acquire a lock on it. Mutual exclusion will not be guaranteed, either, because there may be arbitrarily many threads, each locking a different String
instance without contention.
Combining the two phenomena described above we can also see that there is no guarantee that the object reached on the line synchronized(string)
will be the same one as the one reached from within the synchronized block. Once it happens that this is indeed a different object, your IllegalMonitorStateException
ensues.
In summary, the situation is very similar to the synchronized
block not existing at all.
All of the above problems can be avoided if you keep to the best practice of using dedicated final
variables to refer to objects used for locking. In a nutshell, and fixing the compilation errors in your example, this is what you would have to write:
static String string = "";
static final Object lock = new Object();
public static void main(String[] args) {
Thread t1 = new Thread() {
public void run() {
synchronized (lock) {
... update the string variable ...
lock.notifyAll();
}
}
};
Thread t2 = new Thread() {
public void run() {
synchronized (lock) {
try {
lock.wait();
} catch (Exception e) {
e.printStackTrace();
}
}
}
};
t2.start();
t1.start();
}