user3864457
user3864457

Reputation:

Does the waiting thread revisit the code inside synchronized method

I was reading about thread synchronisation and wait/notify constructs from the tutorial. It states that

When wait is invoked, the thread releases the lock and suspends execution. At some future time, another thread will acquire the same lock and invoke Object.notifyAll, informing all threads waiting on that lock that something important has happened.

Some time after the second thread has released the lock, the first thread reacquires the lock and resumes by returning from the invocation of wait.

AFAIK, if there are multiple threads which can compete for the lock when the first thread is awaken by the notify, any one of them can get to own the lock on that object. My question is, if this first thread itself re-acquires the lock, does it have to start all over from the beginning of the synchronized method (which means, it again executes the code before the while loop checking wait() condition) or does it just pause at the wait() line?

// Does the waiting thread come back here while trying to own the
// lock (competing with others)?
public synchronized notifyJoy() {
    // Some code  => Does this piece of code gets executed again then in case
    // waiting thread restarts its execution from the method after it is notified?
    while (!joy) {
        try {
            // Does the waiting thread stay here while trying to re-acquire
            // the lock?
            wait();
        } catch(InterruptedException e) {}
    }
    // Some other code
}
        

Upvotes: 5

Views: 2773

Answers (2)

Nathan Hughes
Nathan Hughes

Reputation: 96395

A method only gets exited when the thread executing it finishes executing its run method, whether by returning normally or having an exception be thrown that goes uncaught within that run method. The only way for your method to not get executed until one of those things above happens is for the JVM to be killed out from under you (with java.lang.System.exit, killing the java process with kill -9, etc.), or if the method is being run in a daemon thread where the JVM is shutting down. There's nothing weird going on here. The thread that waits gives up the lock and goes dormant, but it doesn't somehow leave off executing the method.

The thread awakened from its call to wait never went anywhere; the whole time that the thread was waiting it was still in the wait method. Before it can leave the wait method it first has to acquire the lock that it gave up in order to start waiting. Then it needs to retest whatever condition it needs to check before it can know whether to keep on waiting.

This is why the guarded blocks tutorial tells you that waits have to be done in a loop:

The invocation of wait does not return until another thread has issued a notification that some special event may have occurred — though not necessarily the event this thread is waiting for:

public synchronized void guardedJoy() {
    // This guard only loops once for each special event, which may not
    // be the event we're waiting for.
    while(!joy) {
        try {
            wait();
        } catch (InterruptedException e) {}
    }
    System.out.println("Joy and efficiency have been achieved!");
}

Note: Always invoke wait inside a loop that tests for the condition being waited for. Don't assume that the interrupt was for the particular condition you were waiting for, or that the condition is still true.

(The wording used by the tutorial is misleading; the word "interrupt" should be "notification". Also it is unfortunate that the tutorial code shown eats the InterruptedException without setting the interrupt flag, it would be better to let the InterruptedException be thrown from this method and not catch it at all.)

If the thread did "start over" then this loop wouldn't be required; your code would start at the beginning of the method, acquire the lock, and test the condition being waited on.

Upvotes: 9

Smith_61
Smith_61

Reputation: 2088

Thread execution starts directly after the call to wait. It does not restart the block from the beginning. wait() can be roughly implemented similar to

public void wait() {
    release_monitor();
    wait_monitor();
    acquire_monitor();
}

This is no where near how it is actually implemented, it is just a rough idea of what goes on behind the scenes. Each Object has a monitor associated with it that can acquired and released. Only one thread can hold the monitor at a time and a thread can acquire a monitor recursively with no issue. A call to wait on an Object releases the monitor allowing another thread to acquire it. The waiting thread then waits until it is woken up by a call to notify/notifyAll. Upon being woken up the waiting thread waits again to require the Object's monitor and returns to the calling code.

Example:

private Object LOCK = new Object;
private int num = 0;
public int get() {
    synchronized( LOCK ) {
        System.out.println( "Entering get block." );

        LOCK.wait();

        return num;
    }
}

public void set( int num ) {
    synchronized( LOCK ) {
        System.out.println( "Entering set block." );

        this.num = num;

        LOCK.notify();
     }
}

"Entering get block." will only be printed once for each call to get()

Upvotes: 0

Related Questions