'synchronized block locking object and wait/notify

According to what I understood, when I use a synchronized block it acquires the lock on an object and releases it when the code block is done executing. In the following code

public class WaitAndNotify extends Thread{

    long sum;

    public static void main(String[] args) {
        WaitAndNotify wan = new WaitAndNotify();
        //wan.start();
        synchronized(wan){
            try {
                wan.wait();
            } catch (InterruptedException ex) {
                Logger.getLogger(WaitAndNotify.class.getName()).log(Level.SEVERE, null, ex);
            }
            System.out.println("Sum is : " + wan.sum);
        }
    }

    @Override
    public void run(){
        synchronized(this){
            for(int i=0; i<1000000; i++){
                sum = sum + i;
            }
            notify();
        }

    }  
}

what happens if the synchronized block inside the run method acquires the lock first? Then the synchronized block inside the main method has to wait (not because of the wait(), because the other thread acquired the lock). After the run method is done executing, won't the main method enter its synchronized block and wait for a notify which it will never get? What did I misunderstand here?



Solution 1:[1]

wait() implicitly exits the respective monitor temporarily and re-enters it upon returning:

See wait()

The current thread must own this object's monitor. The thread releases ownership of this monitor and waits until another thread notifies threads waiting on this object's monitor to wake up either through a call to the notify method or the notifyAll method. The thread then waits until it can re-obtain ownership of the monitor and resumes execution.

That's why and how this sort of synchronization does work at all.

Solution 2:[2]

Yes, it's possible to perform a notify() before a wait() causing a hung thread, so you need to be careful that it can't happen.

For that reason (and others) it's generally better to use the higher level constructs of java.util.concurrent, since they generally give you less possibilities to shoot yourself in the foot.

Solution 3:[3]

You won't see the 'waiting forever' issue here, because you are calling the version of wait() with a timeout; so, after 5 seconds it returns even if it doesn't receive a notify. The 'wait forever' version of the wait() call could indeed exhibit the problem you describe.

Solution 4:[4]

You've got two threads here: your WaitAndNotify (WAN) thread, and Java's main execution thread. Both are vying for the same lock.

If the WAN thread gets the lock first, the main thread will be blocked. Being in a blocked state is NOT the same as being in a wait state. A thread in the wait state will wait for notification before moving forward. A thread in the blocked state will actively try to get the lock when it becomes available (and keep trying until it does).

Assuming the run method executes normally, it will call notify(), which will have no effect because no other threads are currently in a wait state. Even if there were, WAN still holds the lock until it exits the synchronized block of code. Once WAN exits the block, THEN Java would notify a waiting thread (if there was one, which there is not).

At this point, the main execution thread now obtains the lock (it is no longer blocked) and enters the wait state. Now you've used the version of wait that will wait up to 5000 milliseconds before continuing. If you used the vanilla version (wait()) it would wait forever because no other process would notify it.

Solution 5:[5]

Here is a version of the example program changed to introduce a loop that tests a condition variable. This way you avoid bad assumptions about the state of things after a thread re-acquires a lock upon waking from a wait, and there's no order dependence between the two threads:

public class W extends Thread {
    long sum;
    boolean done;

    public static void main(String[] args) throws InterruptedException {
        W w = new W();
        w.start();
        synchronized(w) {
            while (!w.done) {
                w.wait();
            }
            // move to within synchronized block so sum
            // updated value is required to be visible
            System.out.println(w.sum);
        }
    }

    @Override public synchronized void run() {
        for (int i = 0; i < 1000000; i++) {
           sum += i;
        }
        done = true;
        // no notify required here, see nitpick at end
    }
}

It's not sufficient to wait on a notification, for the reason you point out (order dependence, where you're relying on a race condition hoping one thread acquires the monitor before another) as well as for other reasons. For one thing, a thread can wake up from waiting without ever having received a notification, you can't assume that there was a notify call at all.

When a thread waits, it needs to do so in a loop, where in the test on the loop it checks some condition. The other thread should set that condition variable so the first thread can check it. The recommendation that the Oracle tutorial makes is:

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.

Other nitpicks:

  • As your example is written, the JVM is not required to make the changes to your sum variable visible to the main thread. If you add a synchronized instance method to access the sum variable, or access the sum within a synchronized block, then the main thread will be guaranteed to see the updated value of sum.

  • Looking at your logging, there is nothing SEVERE about an InterruptedException, it doesn't mean anything went wrong. An InterruptedException is caused when you call interrupt on a thread, setting its interrupt flag, and that thread is either currently waiting or sleeping, or enters a wait or sleep method with the flag still set. In my toy example at the top of this answer I put the exception in the throws clause because I know it's not going to happen.

  • When the thread terminates it issues a notifyAll that anything waiting on that object will receive (again, that's how join is implemented). It's better style to use Runnable instead of Thread, partly because of this.

  • In this particular example it would make more sense to call Thread#join on the summing thread, rather than calling wait.

Here's the example re-written to use join instead:

public class J extends Thread {
    private long sum;

    synchronized long getSum() {return sum;}

    public static void main(String[] args) throws InterruptedException {
        J j = new J();
        j.start();
        j.join();
        System.out.println(j.getSum());
    }

    @Override public synchronized void run() {
        for (int i = 0; i < 1000000; i++) {
           sum += i;
        }        
    }
}

Thread#join calls wait, locking on the thread object. When the summing thread terminates it sends a notification and sets its isAlive flag to false. Meanwhile in the join method, the main thread is waiting on the summing thread object, it receives the notification, checks the isAlive flag, and realizes it doesn't have to wait anymore, so it can leave the join method and print the result.

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1
Solution 2 Kayaman
Solution 3 BarrySW19
Solution 4 Bobby StJacques
Solution 5