Why must wait() always be in synchronized block
A wait()
only makes sense when there is also a notify()
, so it's always about communication between threads, and that needs synchronization to work correctly. One could argue that this should be implicit, but that would not really help, for the following reason:
Semantically, you never just wait()
. You need some condition to be satsified, and if it is not, you wait until it is. So what you really do is
if(!condition){
wait();
}
But the condition is being set by a separate thread, so in order to have this work correctly you need synchronization.
A couple more things wrong with it, where just because your thread quit waiting doesn't mean the condition you are looking for is true:
You can get spurious wakeups (meaning that a thread can wake up from waiting without ever having received a notification), or
The condition can get set, but a third thread makes the condition false again by the time the waiting thread wakes up (and reacquires the monitor).
To deal with these cases what you really need is always some variation of this:
synchronized(lock){
while(!condition){
lock.wait();
}
}
Better yet, don't mess with the synchronization primitives at all and work with the abstractions offered in the java.util.concurrent
packages.
@Rollerball is right. The wait()
is called, so that the thread can wait for some condition to occur when this wait()
call happens, the thread is forced to give up its lock.
To give up something, you need to own it first. Thread needs to own the lock first.
Hence the need to call it inside a synchronized
method/block.
Yes, I do agree with all the above answers regarding the potential damages/inconsistencies if you did not check the condition within synchronized
method/block. However as @shrini1000 has pointed out, just calling wait()
within synchronized block will not avert this inconsistency from happening.
Here is a nice read..
What is the potential damage if it was possible to invoke
wait()
outside a synchronized block, retaining it's semantics - suspending the caller thread?
Let's illustrate what issues we would run into if wait()
could be called outside of a synchronized block with a concrete example.
Suppose we were to implement a blocking queue (I know, there is already one in the API :)
A first attempt (without synchronization) could look something along the lines below
class BlockingQueue {
Queue<String> buffer = new LinkedList<String>();
public void give(String data) {
buffer.add(data);
notify(); // Since someone may be waiting in take!
}
public String take() throws InterruptedException {
while (buffer.isEmpty()) // don't use "if" due to spurious wakeups.
wait();
return buffer.remove();
}
}
This is what could potentially happen:
A consumer thread calls
take()
and sees that thebuffer.isEmpty()
.Before the consumer thread goes on to call
wait()
, a producer thread comes along and invokes a fullgive()
, that is,buffer.add(data); notify();
The consumer thread will now call
wait()
(and miss thenotify()
that was just called).If unlucky, the producer thread won't produce more
give()
as a result of the fact that the consumer thread never wakes up, and we have a dead-lock.
Once you understand the issue, the solution is obvious: Use synchronized
to make sure notify
is never called between isEmpty
and wait
.
Without going into details: This synchronization issue is universal. As Michael Borgwardt points out, wait/notify is all about communication between threads, so you'll always end up with a race condition similar to the one described above. This is why the "only wait inside synchronized" rule is enforced.
A paragraph from the link posted by @Willie summarizes it quite well:
You need an absolute guarantee that the waiter and the notifier agree about the state of the predicate. The waiter checks the state of the predicate at some point slightly BEFORE it goes to sleep, but it depends for correctness on the predicate being true WHEN it goes to sleep. There's a period of vulnerability between those two events, which can break the program.
The predicate that the producer and consumer need to agree upon is in the above example buffer.isEmpty()
. And the agreement is resolved by ensuring that the wait and notify are performed in synchronized
blocks.
This post has been rewritten as an article here: Java: Why wait must be called in a synchronized block