Java 为什么';t线程等待notify()?

Java 为什么';t线程等待notify()?,java,concurrency,wait,notify,Java,Concurrency,Wait,Notify,为什么线程不等待notify()?线程启动后进入等待池,但在那一刻之后继续执行 public class JavaApplication2 { public static void main(String [] args) { ThreadB b = new ThreadB(); synchronized(b) { b.start(); try { System.out.println("

为什么线程不等待
notify()
?线程启动后进入等待池,但在那一刻之后继续执行

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();
       }
    }
 }

您不能指望等待返回,直到收到通知:“可能会出现中断和虚假唤醒”。通常,当线程继续等待时,您应该将等待调用包装在循环中。

您正在线程对象本身上进行同步,这是错误的用法。发生的情况是,即将死亡的执行线程总是在其
线程
对象上调用
通知
线程.join
。因此,很明显,为什么在有和没有自己的
notify
的情况下都会出现相同的行为


解决方案:使用单独的对象进行线程协调;这是标准做法。

在这两个地方嵌套了同步的{}构造。这些构造似乎在做一些奇怪的事情:线程根本不会对notify作出反应,只有在ThreadB(b)终止时才会恢复。删除此项:

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");
    }
}

上面的代码可能工作正常:当notify()出现时,主线程在5秒钟后,在我们看到ThreadB终止的消息之前恢复。注释掉notify()后,主线程将在10秒后恢复,并在关于线程终止的消息后恢复,因为notify()无论如何都会从其他代码中调用。Marko Topolnik解释了此“幕后”notify()调用的来源和原因。

如果您尝试在任何对象上同步代码,而不是在
ThreadB
上,您将发现它从未终止。这是因为存在对
notify
的隐藏调用

虽然我不知道在什么地方指定了它,但线程在结束时会通知自己。这在
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;
        }
    }
}
公共最终同步无效联接(长毫秒)
抛出中断异常{
长基=System.currentTimeMillis();
long now=0;
如果(毫秒<0){
抛出新的IllegalArgumentException(“超时值为负”);
}
如果(毫秒==0){
while(isAlive()){
等待(0);
}
}否则{
while(isAlive()){
长延迟=毫秒-现在;
如果(delay为终止线程的
线程
对象调用了方法
notifyAll()
。在的描述中奇怪地记录了这一事实,并使用以下语句:

当线程终止时,将调用this.notifyAll方法。建议应用程序不要在线程实例上使用wait、notify或notifyAll


因此,如果你没有明确阅读
join
的描述(你不必阅读),你就不会知道奇怪行为的原因。

在阅读OCP SE 7时,我在等待/通知操作上做了相同的测试,很好。我想我们应该让作者解释一下。

什么通知?我不明白任何通知,除了注释掉的通知。确切地说,线程必须等待永恒,但它不会。try
b.join()
而不是
等待
这并不能回答问题。是的,虚假唤醒只是一个次要问题,许多实现根本没有表现出来。无论你在哪里遇到确定性可复制行为,你都可以100%确定虚假唤醒与此无关。完全同意@Marko。我真的不喜欢menti错误的唤醒。它们当然是可能的,但极为罕见。很可能是try bug的错误。请参阅以下错误答案:+1,用于使用单独的线程进行协调。如果您不知道系统的其他部分对对象做了什么,那么您就不能依赖它进行同步。您是正确的,但不是
notify
,调用了
this.notifyAll
方法。我不理解此注释。
notify
this相同。notify
notifyAll
notify
不同。它们是不同的方法。一般来说,它们不相同,但在您的情况下(等待集中只有一个线程),是的。您似乎使用“伪唤醒”这个术语太过松散了——这是一个非常具体的概念,它不涉及这个问题(或我所见过的任何其他问题)。当线程b终止时,主线程确实会恢复,即使notify()也不例外没有调用,已测试。当然;
notify
无论如何都会调用,正如我在回答中所解释的。这不是虚假的唤醒。也可能看起来如此……我在回答中更改了这句话。