BlueRyse
BlueRyse

Reputation: 29

Threads logic in Java

I'm trying to resolve a university exercise. The class AImpl has a method ma(B b) that creates and runs two threads. These threads have to call mb1() and mb2() (they are simple methods that just print a text, so I didn't include them). The calling thread should then wait for mb1() to terminate before finishing.

My logic is:

The first thread enters and after finishing the execution of b.mb1() starts to wait() on the current object, releasing the mutex. Then the second thread runs and it does the same. When they are both waiting, the calling thread calls notifyAll() on the object, waking both of them. They execute b.mb2() and then terminate.

The problem is that when the first thread starts waiting with object.wait(), the control flow doesn't return on the calling thread and the program enters in a deadlock.

Where is my logic flawed?

public class AImpl{
    public static Object object = new Object();

    public static void main(String[] args) throws InterruptedException {
        BImpl b = new BImpl();
        AImpl.ma(b);
    }

    public static void ma(B b) throws InterruptedException {
        Thread thread = new Thread() {
            @Override
            public void run() {
                b.mb1();

                synchronized(object){
                    try {
                        object.wait();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
                b.mb2();

                System.out.println("Thread finished");
            }
        };

        Thread thread1 = new Thread() {
            @Override
            public void run() {
                b.mb1();

                synchronized(object){
                    try {
                        object.wait();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
                b.mb2();

                System.out.println("Thread finished");
            }
        };
        thread.run();
        thread1.run();

        synchronized(object){
            object.notifyAll();
        }

        System.out.println("Program finished.");
    }
}

Upvotes: 1

Views: 247

Answers (1)

Nathan Hughes
Nathan Hughes

Reputation: 96385

The notify/notifyAll methods tell the scheduler to notify one/all of the threads currently waiting on the lock that notify or notifyAll was called on. But if a thread hasn't started waiting yet then it doesn't get notified.

The solution is to introduce a condition variable that keeps wait from being called if the notifying has happened already. Define it in the same scope as your lock:

public static volatile boolean ready = false;

Then use it to guard the wait block, like this:

while (!ready) {
    object.wait();
}

The code calling notify/notifyAll needs to set the variable (it doesn't matter what order you do it in because the notification doesn't happen until the lock is released):

synchronized (object) {
    ready = true;
    object.notifyAll();
}

What happens:

If the waiting thread gets to the waiting part before the notifying thread does its notifying, then the waiting thread finds ready is false, so it enters the wait method, releases the lock, and stays there. Then the notifying thread changes the flag to true and wakes up the waiting thread, which can leave the wait, reacquire the lock, and then leave the loop now that the flag is set.

But if the notifying thread does its notify before the other thread waits, that's ok, because the ready flag now prevents the thread from entering the wait, it can skip over it.

Further reading: https://docs.oracle.com/javase/tutorial/essential/concurrency/guardmeth.html

Upvotes: 2

Related Questions