Why is that sleeping inside a thread causes problems with `notify`?

375 views Asked by At

Driver.java

public class Driver {

    static Object obj = new Object();

    public static void main(String [] args) throws InterruptedException
    {
        Thread thr = new Thread(new Runnable(){

            @Override
            public void run() {
                System.out.println("Thread 1: Waiting for available slot.");
                synchronized(obj){

                    try {
                        obj.wait();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }

                    System.out.println("Thread 1: Found slot!");

                    long x = 0;
                    while(x < Integer.MAX_VALUE) x++;

                    System.out.println("Thread 1: Completed processing.");
                    System.out.println("Thread 1: Notifying other waiting threads.");

                    obj.notify();
                }
            }

        });

        Thread thr2 = new Thread(new Runnable(){

            @Override
            public void run() {
                System.out.println("Thread 2: Waiting for available slot.");
                synchronized(obj){
                    try {
                        obj.wait();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }

                    System.out.println("Thread 2: Found slot!");

                    long x = 0;
                    while(x < Integer.MAX_VALUE) x++;

                    System.out.println("Thread 2: Completed processing.");
                    System.out.println("Thread 2: Notifying other waiting threads.");

                    obj.notify();
                }
            }

        });

        thr.start();
        thr2.start();

        System.out.println("Main Thread: All processing units busy.");
        // Thread.sleep(2000); // Enable this and disable the other Thread.sleep(...) and NOW we are good. But again, 'why?' is the question.

        synchronized(obj){
            Thread.sleep(2000); // This causes a failure. Move it outside the synchronized and it will work why?
            System.out.println("Main Thread: Found ONLY 1 available slot.");
            obj.notify();

            obj.wait(); // JVM should catch this as the last request so it has the least priority.
            System.out.println("Main Thread: Finished and exiting..."); 
        }
    }
}

The code above will not notify the Threads because of the following line:

Thread.sleep(2000); // This causes a failure. Move it outside the synchronized and it will work why?

Please take a look at this line in context with the whole class. I am having hard time pinpointing to the reason why this simple proof-of-concept would fail if that line is placed inside ther synchronized block in the Main Thread.

Thank you

4

There are 4 answers

0
Radiodef On BEST ANSWER

The problem is not the sleep but rather that the main thread almost always acquires the lock before one (and occasionally both) of the created threads does. If you print just inside the synchronized blocks it's much more clear what is going on:

synchronized(obj) {
    System.out.println("this thread acquired the lock");

You'll see the output is almost always Thread #1, then the main thread, and finally Thread #2 after Thread #1 completes (but main has already returned).

If you run it enough times sometimes both child threads do acquire the lock first and it completes.

The reason moving the sleep to outside the synchronized block in the main thread works is it allows both child threads to reach their respective wait statements.

0
yushulx On

sleep holds the lock, but wait doesn't. so when your main thread is sleeping, both thr and thr2 can't get the lock until main thread notifies them. At that moment, they start to wait and can't receive any notify()

0
TwoThe On

The problem is that sleep does not release the monitor, that is: while the main thread is sleeping, all the other threads cannot enter the synchronized block, so they are basically sleeping with the main thread.

The moment the main thread wakes up, it does notify, but since no one yet entered the wait() position, no one is listening. Then the main thread waits and therefore releases the monitor, so now all threads can proceed to the wait() state, but no one is left to wake them up. -> Deadlock

0
aalku On

Read the doc.

Wakes up a single thread that is waiting on this object's monitor.

If it is sleeping then it is not waiting.

There is other related problem, it is not possible to reach the notify line while the other thread is in the sleep as it keeps the monitor (lock) and the other thread can't run inside the synchronized block. This is always that way as both wait and notify must be run inside related syncrhonized blocks (against the same monitor).