Why doesn't thread wait for notify()?

2020-02-10 09:03发布

Why doesn't thread wait for notify()? The thread starts and then goes to the waiting pool, but it proceeds to execute after that moment.

public class JavaApplication2 {
    public static void main(String [] args) {
       ThreadB b = new ThreadB();
       synchronized(b) {
           b.start();
           try {
              System.out.println("1");
              b.wait();
         } catch (InterruptedException e) {}
          System.out.println("Total is: " + b.total);
       }
     }
 }
  class ThreadB extends Thread {   
    int total;
      @Override
    public void run() {
        synchronized(this) {
            total += 1;
            //notify();
       }
    }
 }

6条回答
小情绪 Triste *
2楼-- · 2020-02-10 09:24

The method notifyAll() is invoked for the Thread object of the terminating thread. This fact is strangely documented in the description of the Thread.join, with the following sentence:

As a thread terminates the this.notifyAll method is invoked. It is recommended that applications not use wait, notify, or notifyAll on Thread instances.

Thus, if you don't explicitly read the description of join, which you don't necessarily have to, you don't get to know the reason for the strange behavior.

查看更多
贪生不怕死
3楼-- · 2020-02-10 09:24

I was doing the same testing on the wait/notify opertaions while reading OCP SE 7, good catch. I think we should let the authoer to explain.

查看更多
Explosion°爆炸
4楼-- · 2020-02-10 09:28

You cannot depend on not returning from wait until a notify: "interrupts and spurious wakeups are possible". In general, you should wrap a wait call in a loop while the thread should go on waiting.

查看更多
Bombasti
5楼-- · 2020-02-10 09:29

You have nested synchronized {} constructs in the two places. These constructs seem doing something weird: the thread does not react into notify at all and only resumes when ThreadB (b) terminates. Remove this:

public class JavaApplication2 {
    public static void main(String[] args) {
        ThreadB b = new ThreadB();
        b.start();
        try {
            System.out.println(" ### Waiting for notify");
            synchronized (b) {
                b.wait();
            }
            System.out.println(" ### Notified");
        } catch (InterruptedException e) {
        }
        System.out.println("### Total is: " + b.total);
    }
}

class ThreadB extends Thread {
    int total;

    @Override
    public void run() {
        total += 1;
        System.out.println(" *** Ready to notify in 5 secs");
        try {
            Thread.sleep(5000);
        } catch (InterruptedException e) {
        }
        System.out.println(" *** Notification sent");
        synchronized (this) {
            notify();
        }
        System.out.println(" *** 5 sec post notification");
        try {
            Thread.sleep(5000);
        } catch (InterruptedException e) {
        }
        System.out.println(" *** ThreadB exits");
    }
}

The code above probably works correctly: with notify() present the main thread resumes after 5 seconds and before we see the message that ThreadB terminates. With notify() commented out the main thread resumes after 10 seconds and after the message about the termination of the ThreadB because notify() is called anywhay from the other code. Marko Topolnik explains why and from where this "behind the scene" notify() call comes from.

查看更多
Rolldiameter
6楼-- · 2020-02-10 09:40

If you try your code synchronizing on any object other that ThreadB you will find it never terminates. This is because there is a hidden call to notify.

Although I am not aware of anywhere that this is specified, Thread notifies itself when it ends. This is implicit in the way the join method is implemented. This is the code for join:

public final synchronized void join(long millis)
throws InterruptedException {
    long base = System.currentTimeMillis();
    long now = 0;

    if (millis < 0) {
        throw new IllegalArgumentException("timeout value is negative");
    }

    if (millis == 0) {
        while (isAlive()) {
            wait(0);
        }
    } else {
        while (isAlive()) {
            long delay = millis - now;
            if (delay <= 0) {
                break;
            }
            wait(delay);
            now = System.currentTimeMillis() - base;
        }
    }
}

(From the JDK7 source code)

As you can see, the calls to wait only make sense if somewhere there is a call to notify that is called after the thread ends. The same call to notify is what allows your program to terminate.

查看更多
爷、活的狠高调
7楼-- · 2020-02-10 09:41

You are synchronizing on the thread object itself, which is wrong usage. What happens is that the dying thread-of-execution always calls notify on its Thread object: Thread.join relies on this. Therefore it is clear why you get the same behavior with and without your own notify in there.

Solution: use a separate object for thread coordination; this is the standard practice.

查看更多
登录 后发表回答