【问题标题】:Why doesn't thread wait for notify()?为什么线程不等待 notify()?
【发布时间】:2013-01-06 21:16:08
【问题描述】:

为什么线程不等待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();
       }
    }
 }

【问题讨论】:

  • 什么通知?我没有看到任何通知,除了注释掉的那个。
  • 没错,线程必须等待永恒,但事实并非如此。
  • 试试b.join() 而不是wait

标签: java concurrency wait notify


【解决方案1】:

您正在线程对象本身上进行同步,这是错误的用法。发生的情况是,死去的执行线程总是在其Thread 对象上调用notifyThread.joinrelies on this。因此,很清楚为什么在有和没有你自己的notify 的情况下你会得到相同的行为。

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

【讨论】:

  • +1 用于使用单独的线程进行协调。如果您不知道系统的其他部分正在处理该对象,那么您就不能依赖它进行同步。
  • 你是对的,但不是notify,调用的是this.notifyAll方法。
  • 我不明白这条评论。 notifythis.notify 相同。
  • notifyAllnotify 不同。它们是不同的方法。
  • 一般情况下不一样,但在你的情况下(只有一个线程在等待集中),它是
【解决方案2】:

为终止线程的Thread 对象调用方法notifyAll()。这个事实奇怪地记录在Thread.join的描述中,有以下句子:

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

因此,如果您没有明确阅读 join 的描述,您不一定必须阅读,您就不会知道这种奇怪行为的原因。

【讨论】:

    【解决方案3】:

    您不能依赖从等待直到通知不返回:“中断和虚假唤醒是可能的”。一般来说,你应该在一个循环中包装一个等待调用,而线程应该继续等待。

    【讨论】:

    • 这不能回答问题。
    • 是的,虚假唤醒只是一个边缘问题,许多实现根本没有表现出来。无论您在哪里遇到确定性可重现的行为,您都可以 100% 确定虚假唤醒与它无关。
    • 完全同意@Marko。我真的不喜欢提到虚假唤醒。它们当然是可能的,但极为罕见。尝试错误更有可能是错误的。看到这个错误的答案:stackoverflow.com/a/2960667/179850
    【解决方案4】:

    如果您尝试在ThreadB 以外的任何对象上同步您的代码,您会发现它永远不会终止。这是因为有一个对notify 的隐藏调用。

    虽然我不知道在任何地方指定了此项,但 Thread 会在结束时通知自己。这隐含在 join 方法的实现方式中。这是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;
            }
        }
    }
    

    (来自JDK7源代码)

    如您所见,只有在线程结束后调用notify 的某个地方调用wait 才有意义。对notify 的相同调用是允许您的程序终止的原因。

    【讨论】:

      【解决方案5】:

      您在这两个地方嵌套了同步 {} 构造。这些构造似乎在做一些奇怪的事情:线程根本没有对通知做出反应,只有在 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 秒后恢复,并且 发出有关 ThreadB 终止的消息,因为 notify() 是从其他代码中调用的。 Marko Topolnik 解释了这个“幕后” notify() 调用的原因和来源。

      【讨论】:

      • 您似乎对“虚假唤醒”一词的使用过于松散——这是一个非常具体的概念,它不涉及这个问题(或我见过的任何其他关于 SO 的问题) .
      • 主线程确实会在线程 b 终止时恢复,即使 notify() 没有被调用,经过测试。
      • 当然;正如我在回答中解释的那样,无论如何都会调用notify。这不是虚假的唤醒。
      • 也可以这样看……我在回答中改了那句话。
      【解决方案6】:

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

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2019-11-12
        • 2017-07-19
        相关资源
        最近更新 更多