Search code examples
javamultithreadingsynchronizedthread-synchronization

Synchronized block in the main method


In the below code about the synchronisation between threads, according to the output generated why is the control being transferred to the execution of the new thread despite the lock being acquired for the same object "dt" in the main method ?

public class DemoThread extends Thread {

public DemoThread() {
}

public void run() {
    int i=0;
    synchronized(this) {
        while(++i<=5) {
            sum=i;
            try{
                sleep(1000);
                System.out.println("Woke up from sleep");
               if(i>=2) this.notify();
            }catch(InterruptedException ie) {
                ie.printStackTrace();
                System.exit(1);
            }
        }
    }
}
private static int sum;    

public static void main(String... args) {

    DemoThread dt = new DemoThread();
    dt.start();

    synchronized(dt) {
        try{
            System.out.println("main here");
            dt.wait();
            System.out.println("main here again");
            System.out.println("sum = " + sum);
        }catch(InterruptedException ie){
            ie.printStackTrace();
            System.exit(1);
        }
    }        
}
}

Output :

main here
Woke up from sleep
Woke up from sleep
Woke up from sleep
Woke up from sleep
Woke up from sleep
main here again
sum = 5

EDIT: I think I was able to find one of the possible flow of the code to explain the output:

1.Main thread enters in the Sync block in the main method.

2.call to the wait is made. Lock released on the dt object

3.New thread enters the while loop as it has the lock on the object dt

4.Thread.Sleep is executed and it doesn't release the lock

5.notify call is made but doesnot wakr the main thread(?)

6.New and the main thread finish the execution

Please correct me if I am wrong


Solution

  • You are close :

    1.Main thread enters in the Sync block in the main method.

    2.call to the wait is made. Lock released on the dt object

    3.New thread enters the while loop as it has the lock on the object dt

    4.Thread.Sleep is executed and it doesn't release the lock

    5.notify call is made but doesnot wake the main thread(?)

    6.New and the main thread finish the execution

    Until the step 4, it is correct.

    Here is what it happens at the step 5 :

    notify() is invoked and the main() thread is so notified.
    But it will not have a chance to run again right now.
    Why ? Because the DemoThread thread doesn't release the lock.

    The notify() method is indeed executed in a loop inside a synchronized statement.

    synchronized (this) {
        while (++i <= 5) {
            sum = i;
            try {
                sleep(1000);
                System.out.println("Woke up from sleep");
                if (i >= 2) {
                    notify();
                }
    
            } catch (InterruptedException ie) {
                ie.printStackTrace();
                System.exit(1);
            }
        }
    

    And according to Object.notify() javadoc :

    The awakened thread will not be able to proceed until the current thread relinquishes the lock on this object. The awakened thread will compete in the usual manner with any other threads that might be actively competing to synchronize on this object; for example, the awakened thread enjoys no reliable privilege or disadvantage in being the next thread to lock this object.

    So the main() thread could run only as the run() method DemoThread is terminated.

    To let the main() thread to run again, you could reverse in the DemonThread run() method, the synchronized statement and the while statement.
    You should also make this thread sleep a little bit to let the main() thread to run again.

    public void run() {
        int i = 0;
    
        while (++i <= 5) {
            // let a chance for other threads
            try {
                Thread.sleep(100);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            synchronized (this) {
                sum = i;
                try {
                    sleep(1000);
                    System.out.println("Woke up from sleep");
                    if (i >= 2) {
                        notify();                   
                    }
    
                } catch (InterruptedException ie) {
                    ie.printStackTrace();
                    System.exit(1);
                }
            }
        }
    }
    

    Now as i >= 2, as previously, other threads are notified but as the thread leaves the lock as it loops on the while and then sleep 100 ms, the main() thread can run again.

    Here is the output :

    main here

    Woke up from sleep

    Woke up from sleep

    main here again

    sum = 2

    Woke up from sleep

    Woke up from sleep

    Woke up from sleep