1

Not sure why b.wait() doesn't wait the main thread. If we create a dummy object, Object a = new Object(); it waits. If I extend ThreadA with Thread and create a instance for ThreadA and lock using ThreadA reference it works. But why the below wait() doesn't work

package com.aircell;

public class ThreadA{
    public static void main(String[] args){
        ThreadB b = new ThreadB();
        b.start();
        System.out.println("who is this thread");
        synchronized(b){
            try{
                System.out.println("Waiting for b to complete...");
                b.wait();
            }catch(InterruptedException e){
                e.printStackTrace();
            }

            System.out.println("Total is: " + b.total);
        }
    }
}

class ThreadB extends Thread{
    int total;
    @Override
    public void run(){
        synchronized(this){
            for(int i=0; i<100 ; i++){
                total += i;

            }

        }
        System.out.println("done thread");
    }
}
  • 1
    If you want main thread to wait until `ThreadB` finishes, you'd better use `b.join ()` instead of all that stuff with `wait ()` – Ivan Jun 21 '18 at 16:06
  • @Ivan His question isn't about the better approach. Rather, why what he has written is not following Java spec. – Nishit Jun 21 '18 at 16:09
  • Yes. I m just trying to understand the behavior here, if i remove the for loop in ThreadB and provide an infinite while loop, it just prints "who is this thread" and the program waits abruptly – Rajkumar Sundaram Jun 21 '18 at 16:13
  • 1
    What do you mean by `doesn't wait`? Also `wait()` waits for calls of `notify()` or `notifyAll()` on the same object not for thread to finish. And your program doesn't finish in case of infinite loop due to 2 things: if `ThreadB` starts its infinite loop then main thread will wait on `synchronized(b)` forever because `ThreadB` holds that lock else main thread will wait in `wait()` for `notify/notifyAll` which will never be called. And JVM will not exit because non-daemon `ThreadB` will never terminate – Ivan Jun 21 '18 at 16:22
  • No. I dont think that is the behavior, if main thread waits then it should have printed the "waiting for b to complete..." before going to wait, but it doesn't which means the main thread didn't go into the synchronized block itself. Please see my previous response, it just prints "who is this thread" – Rajkumar Sundaram Jun 21 '18 at 16:27
  • What I do noticed, if the thread starts executing (b here), it doesn't allow the other thread (main thread as per this example) to go into the sync block of B. Once B finishes it actually allows it. – Rajkumar Sundaram Jun 21 '18 at 16:30
  • Also I do noticed, as per the same example above, if I make the main thread to sleep for few seconds before going to the sync block by the time B finishes its execution, then main thread into the sync block and gets into infinite wait. Need to figure it out how this delay matters and works as expected. – Rajkumar Sundaram Jun 21 '18 at 16:31
  • @RajkumarSundaram put the `start` into the synchronized block and you shouldn't have all these effects you've seen. But still you will end up in an endless wait because `notifyAll` is never called. – Lothar Jun 21 '18 at 16:36

3 Answers3

0

First of all: If I try out your class Here[TM] I get the following output:

who is this thread
Waiting for b to complete...
done thread
Total is: 4950

So it does work which is a bit surprising, but I think the following sentence in the Javadoc of wait seems to happen here:

As in the one argument version, interrupts and spurious wakeups are possible, and this method should always be used in a loop.

The following change should give you the behavior you wanted to implement:

public class ThreadA{
    public static void main(String[] args){
        ThreadB b = new ThreadB();
        System.out.println("who is this thread");
        synchronized(b){
            b.start();
            try{
                System.out.println("Waiting for b to complete...");
                while (!b.finished) {
                    b.wait();
                }
            }catch(InterruptedException e){
                e.printStackTrace();
            }

            System.out.println("Total is: " + b.total);
        }
    }
}

class ThreadB extends Thread{
    int total;
    boolean finished;
    @Override
    public void run(){
        synchronized(this){
            try {
                for(int i=0; i<100 ; i++){
                    total += i;
                }
            }
            finally {
                finished = true;
                this.notifyAll();
            }
        }
        System.out.println("done thread");
    }
}
Lothar
  • 5,323
  • 1
  • 11
  • 27
  • Or just use `try { b.join(); } catch (whatever join throws) {}` without all those `wait/notify` stuff. – Ivan Jun 21 '18 at 16:46
0

Wonderful question. Took me several hours to figure this out.

When a thread finishes execution, the thread object (in this case, object - "b") is notified by the JVM.

So, in the main thread, if the execution has reached b.wait() before ThreadB finishes, then an implicit b.notify() is called which notifies the main thread. The sequence that's happening in your code is either of the following 2 :

Case I :

  • b.start() is called.
  • However, before threadB can execute, main thread enters synchronized block, acquires lock, and calls b.wait().
  • b.wait() releases the lock, main thread is descheduled, and threadB begins execution.
  • threadB completes execution and lock is released.
  • Since threadB is completed, notify() is called on the Thread object. i.e., JVM triggers an implicit b.notify()
  • Main thread is awoken and completes it's task.

Case II :

  • b.start() is called.
  • threadB execution begins before main thread can acquire lock. Main thread is blocked on synchronized(b)
  • threadB completes execution and lock is released.
  • Main thread acquires lock and enters synchronization block and b.wait() is called.
  • Since threadB is completed, notify() is called on the Thread object. i.e., JVM triggers an implicit b.notify()
  • b.wait() releases the lock, main thread is descheduled, and 2nd thread begins execution.
  • Main thread is awoken and completes it's task.

However, if you add a delay in the main thread. Sufficient enough for ThreadB to finish before main thread can execute b.wait(), you will see that the main thread waits. If you put Thread.sleep(2000);

Nishit
  • 1,276
  • 2
  • 11
  • 25
-2

wait() method can not guarantee the execution to wait for first to finished but if you want one thread to fin