Thread thread = new Thread(() -> {
synchronized (this){
try {
this.wait();
System.out.println("Woke");
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
});
thread.start();
TimeUnit.SECONDS.sleep(1);
this.notify();
When calling notify
it says
java.lang.IllegalMonitorStateException: current thread is not owner
The typical usage of notify
is that you call it and then you release the lock implicitly (by leaving the synchronized block) so that the waiting threads may re-acquire the lock.
But code above calls notify
even before it has the lock, so other threads can just try to acquire the lock, why not? I think the holding the lock is not necessary.
CodePudding user response:
I think the holding the lock is not necessary.
It is necessary because the javadoc for Object.notify()
says it is necessary. It states:
"This method should only be called by a thread that is the owner of this object's monitor. A thread becomes the owner of the object's monitor in one of three ways:
- By executing a
synchronized
instance method of that object.- By executing the body of a
synchronized
statement that synchronizes on the object.- For objects of type
Class
, by executing asynchronized static
method of that class."
But your real question is why is necessary.
To answer that, we need to understand that Java's wait
/ notify
mechanism is primarily designed for implementing condition variables. The purpose of a condition variable is to allow one thread to wait for a condition to become true and for another thread to notify it that this has occurred. The basic pattern for implementing condition variables using wait()
/ notify()
is as follows:
// Shared lock that provides mutual exclusion for 'theCondition'.
final Object lock = new Object();
// Thread #1
synchronized (lock) {
// ...
while (! theCondition) { // One reason for this loop will
// become later ...
lock.wait();
}
// HERE
}
// Thread # 2
synchronized (lock) {
// ...
if (theCondition) {
lock.notify();
}
}
This when thread #1 reaches // HERE
, it knows that theCondition
is now true. Furthermore it is guaranteed the current values variables that make up the condition, and any others controlled by the lock
monitor will now be visible to thread #1.
But one of the prerequisite for this actually working is that both thread #1 and thread #2 are synchronize on the same monitor. That is what guarantees the visibility of the values. Since the above only works reliably when threads #1 and #2 follow the monitor, the Java designers decided to enforce this in implementations of the wait()
and notify
methods themselves. Hence the javadoc that I quoted above.
Now ... your use-case for wait()
/ notify()
is simpler. No information is shared between the two threads ... apart from the fact that the notify occurred.
But it is necessary. And to understand why you need to consider the consequences of this from the javadoc for the wait()
methods:
"A thread can wake up without being notified, interrupted, or timing out, a so-called "spurious wakeup". While this will rarely occur in practice, applications must guard against it ..."
What this means is that your example is incorrect ... in theory, if not in reality. A spurious wakeup could cause the child thread to be woken before the main thread's sleep(...)
completes!!!
A corrected version of your example (i.e. one that is not vulnerable to spurious wakeups) looks like this:
final Object lock = new Object;
boolean wakeUp = false;
Thread thread = new Thread(() -> {
synchronized (lock){
try {
while (!wakeUp) {
this.wait();
}
System.out.println("Woke");
} catch (InterruptedException e) {
// This is questionable, depending on the context
throw new RuntimeException(e);
}
}
});
thread.start();
TimeUnit.SECONDS.sleep(1);
synchronized (lock) {
wakeUp = true;
this.notify();
}
Note that there are simpler ways to do this using various java.concurrent.*
classes
CodePudding user response:
The case where using synchronized makes sense is where the thing using the lock has state that needs to be protected. In that case the lock has to be held while notifying because there are going to be state changes that go along with the notification, so that requiring notify to be called with the lock makes sense.
Using wait/notify without state that indicates when the thread should wait is not safe, it allows race conditions that can result in hanging threads, or threads can stop waiting without having been notified. It really isn't safe to use wait and notify without keeping state.
If you have code that doesn't otherwise need that state, then synchronized is an overcomplicated/tricky/buggy solution. In the case of the posted code example you could use a CountdownLatch instead, and have something that is simple and safe.