【问题标题】:What happen when we use wrong notify()?当我们使用错误的 notify() 时会发生什么?
【发布时间】:2014-05-25 11:39:43
【问题描述】:

如果其他线程等待不同的条件,我们应该调用notifyAll(),这样每个其他线程都有机会获得执行。

但是我们犯了一个错误,假设所有线程都在等待相同的条件,我们调用notify(),让JVM选择一个线程,但是选择的线程不能运行,因为条件仍然不能满足,那会发生什么?

所有线程停止运行?还是JVM继续选择另一个线程唤醒,就像notifyALL()一样?

【问题讨论】:

  • but the selected thread cannot run, because the condition still cannot satisfy请详细说明?
  • 线程 a:while(a>0),线程 b:while(b>0),线程 c:while(c>0)...
  • notify 唤醒一个在该对象上调用了wait 的线程。这里没有什么可以满足的。
  • @lovespring 好吧,在这种情况下,线程很可能会再次 wait 并且也许会唤醒其他线程。显示一些代码。

标签: java multithreading jvm


【解决方案1】:

JVM 无法继续选择另一个线程来唤醒,因为重新进入wait 的决定发生在更高级别的逻辑上,超出了线程调度程序的“范围”。所以是的,在您描述的场景中,所有线程都将继续等待。

【讨论】:

    【解决方案2】:

    在这种情况下,您应该有一个deadlock,因为所有线程都将处于等待状态。

    【讨论】:

      【解决方案3】:
      thread a: while(a>0), thread b: while(b>0), thread c: while(c>0)
      

      那是你的代码逻辑有缺陷。是的,它将导致deadlock。因为你有 while 线程会继续吃掉你的 CPU 并且因为它持有锁,所以没有其他线程可以获取它。

      所有线程停止运行?还是JVM继续选择另一个线程唤醒,就像notifyALL()一样?

      将获得关键区域锁定的线程将进入无限循环。所有其他线程将继续等待。只要某个线程持有锁,任何 JVM 都不会通知其他线程。

      【讨论】:

        【解决方案4】:

        为同一个监视器等待()的所有线程都应该在循环中等待相同的条件变为真。通知()监视器的线程应该只在使条件为真之后这样做。如果你遵循这些规则,那么至少有一个线程会被唤醒并能够取得进展。

        例如;

        final Object lock = new Object();
        final AtomicBoolean timeToGo = new AtomicBoolean(false);
        
        Runnable waiter = new Runnable() {
            public void run() {
                synchronized(lock) {
                    while (! timeToGo.get()) {
                        lock.wait();
                    }
                    System.out.println("Hello!");
                    timeToGo.set(false);
                }
            }
        };
        
        Runnable notifier = new Runnable() {
            public void run() {
                synchronized(lock) {
                    timeToGo.set(true);
                    lock.notify();
                }
            }
        };
        

        如果有五个线程挂在 waiter.run() 的 wait() 方法中,并且有一个线程调用了 notifier.run(),那么这五个线程中恰好有一个会打印“Hello!”并退出。其他人将继续等待下一次 notifier.run() 调用。

        如果您希望在调用 notifier.run() 时所有线程都打印 hello,则将 lock.notify() 调用更改为 lock.notifyAll(),并从 waiter.run() 中删除设置的行标志恢复为假。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2023-04-09
          • 1970-01-01
          • 1970-01-01
          • 2011-03-22
          • 2010-12-10
          • 2011-04-17
          相关资源
          最近更新 更多