views:

380

answers:

3

Hi gents,

We all know that in order to invoke Object.wait() , this call must be placed in synchronized block,otherwise,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 synch block, retaining it's semantics - suspending the caller thread ?

Thanks in advance

+6  A: 

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.

And there's another thing wrong with it because you can get spurious wakeups, and you could get a notify(), but a third thread makes the condition false again by the time the waiting thread wakes up. So what you really need is always some variation of this:

synchronized(lock){
    while(!condition){
        wait();
    }
}

Better yet, don't mess with the synchronization primitives at all and work with the abstractions offered in the java.util.concurrent packages.

Michael Borgwardt
There's a detailed discussion here as well, saying essentially the same thing. http://coding.derkeiler.com/Archive/Java/comp.lang.java.programmer/2006-01/msg01130.html
Willie Wheeler
+2  A: 

Let's illustrate the issue that could arise if wait() could be called outside of a synchronized block with a concrete example.

Suppose we wanted to implement a blocking queue (I know, there is already one in the api :)

A first attempt (without synchronization using the synchronized keyword) 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. The situation could even result in a dead-lock.

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 occur in synchronized blocks.

aioobe
A: 

If you have the need for a wait outside of the context of monitors, you might be looking for sleep.

rsp