javamultithreadingconcurrencywaitnotify

Using synchronized blocks, notify() and wait() the right way


I'm curious to submit here a short example I made and hopefully have someone able to explain to me one thing: is it possible to use the wait() and notify() inside a synchronized block without having to declare threads explicitly? (AKA: not using dedicated threads).

Here's the example:

public class mutex {
    
    private Object mutex = new Object(); 
    
    public mutex(Object mutex) {
        this.mutex = mutex; 
    }
    
    public void step1() throws InterruptedException {
        System.out.println("acquiring lock"); 
        synchronized(mutex) {
            System.out.println("got in sync block"); 
            System.out.println("calling wait"); 
            mutex.wait(); 
            System.out.println("wait finished "); 
        }
    }
    
    public void step2() throws InterruptedException{
        System.out.println("acquiring lock");
        synchronized(mutex){
            System.out.println("got in sync block"); 
            System.out.println("calling notify"); 
            mutex.notify();
            System.out.println("notify called"); 
        }
    }

Those two simple step are just prints for logging and what should be happening. The idea is to be able to call a wait() in step1 and be able to complete the call once step2 has been called with its notify().

Now, as far as I understood the whole thing, this is the right way to do what I want to do:

public void go1() {
        Object mutex = new Object(); 
        mutex m = new mutex(mutex); 
        
        
        Thread t1 = new Thread(()->{
            try {
                m.step1();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }); 
        
        Thread t2 = new Thread(()->{
            try {
                Thread.sleep(1000);
                m.step2();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }); 
        t1.start(); 
        t2.start();
    }

and finally the main

public static void main(String[] args) {
    Object mutex = new Object();
    
    new mutex(mutex).go1();
    //new mutex(mutex).go2();
}

The above code works and shows what I am expecting:

acquiring lock
got in sync block
calling wait
acquiring lock
got in sync block
calling notify
notify called
wait finished 

I get why it works. This is what I expected to happen and how I have been taught to do this. The question comes now as I will paste the second variant of the main function I wanted to test - this one just hangs when the wait() is called.

public void go2() {
    Object mutex = new Object(); 
    mutex m = new mutex(mutex); 
    
    try {
        m.step1();
        Thread.sleep(1000);
        m.step2();
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
}

Why does this hang? Is it because there is just one thread doing everything and it goes into waiting state after the wait() is called? I know that when wait is called on the monitor object it should also release the lock, so why in this case the program can't get to call the step2()? Is there a way to use the my second go() function to achieve this process or is it impossible for it to work?

TLDR just so I am making sure I can be understood: do I have to use dedicated threads to also use properly wait() and notify()? Because I seem to get deadlocks if I don't.

Thank you.


Solution

  • Once you call mutex#wait, the current thread is added to the wait set of object mutex. And thread will not execute any further instructions until it has been removed from mutex's wait set. That's why step2 cannot be executed by the current thread.

    The current thread will be removed from the wait set and resume if other threads call mutex#notify/notifyAll. See JLS#WAIT for all situations in which the current thread can resume..