Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why must wait() always be in synchronized block

We all know that in order to invoke Object.wait(), this call must be placed in synchronized block, otherwise an IllegalMonitorStateException is thrown. But what's the reason for making this restriction? I know that wait() releases the monitor, but why do we need to explicitly acquire the monitor by making particular block synchronized and then release the monitor by calling wait()?

What is the potential damage if it was possible to invoke wait() outside a synchronized block, retaining it's semantics - suspending the caller thread?

like image 239
diy Avatar asked May 06 '10 08:05

diy


People also ask

Why must wait () method be called from the synchronized block?

wait method tells the current thread (thread which is executing code inside a synchronized method or block) to give up monitor and go to waiting state. notify method wakes up a single thread that is waiting on this object's monitor.

Is it possible to call wait () in a non synchronized block?

If you need to call wait(), notify(), or notifyAll() from within a non-synchronized method, then you must first obtain a lock on the object's monitor. If you don't, an exception will be generated when an attempt is made to call the method in question.

Does wait () Release lock from synchronized block?

The wait function doesn't release "all locks", but it does release the lock associated with the object on which wait is invoked.

Is it necessary to call wait and notify from synchronized method?

Explanation: Option A is correct because the notifyAll() method (along with wait() and notify()) must always be called from within a synchronized context. Option B is incorrect because to call wait(), the thread must own the lock on the object that wait() is being invoked on, not the other way around.


2 Answers

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:

  1. A consumer thread calls take() and sees that the buffer.isEmpty().

  2. Before the consumer thread goes on to call wait(), a producer thread comes along and invokes a full give(), that is, buffer.add(data); notify();

  3. The consumer thread will now call wait() (and miss the notify() that was just called).

  4. 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

like image 52
aioobe Avatar answered Oct 02 '22 17:10

aioobe


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.

like image 43
Michael Borgwardt Avatar answered Oct 02 '22 17:10

Michael Borgwardt