【问题标题】:Does notifyAll removes wait from loop without checking condition?notifyAll 是否在不检查条件的情况下从循环中删除等待?
【发布时间】:2025-12-06 17:00:02
【问题描述】:
public class ShareResource {

private int n = 0;

public synchronized void p() throws InterruptedException {
    while (n > 0) {
    wait();
    }
    n++;
}

public synchronized void r() {
  n = 0;
  notifyAll();
}

}

如果我用这个资源启动了两个线程并且它们都在 wait() 并且我在资源中调用了方法 r() 会唤醒两个线程而不检查条件吗? 是否会在两个线程中读取代码直到方法结束?在“同一”时间?

【问题讨论】:

  • 两个线程都会唤醒,但由于代码是synchronized,一次只能运行一个线程,因此其中一个线程会在其他线程执行while (n > 0)之前执行n++

标签: java multithreading wait synchronized notify


【解决方案1】:

两个等待线程都会收到通知。两个等待线程在循环内唤醒。两者都必须检查条件才能退出循环。但是其中一个等待的线程会先运行,然后会在另一个线程上关上门。

当一个等待线程被通知它仍然在等待方法中时,它不能离开等待方法,直到它获得锁。 (请记住,等待是在同步方法中进行的,线程在开始等待时放弃了对 ShareResource 对象的锁定。)当然,一次只有一个线程可以获取锁定。

在两个被通知的线程中,第一个获得锁的将离开等待方法,然后检查循环条件。它发现 n 为 0 并且它可以退出循环。然后线程递增 n。所有这一切都发生在持有锁的情况下,因此没有其他线程可以干扰。

一直以来第二个线程都被阻塞了;它被与第一个线程相同的通知唤醒,但它无法获得锁。一旦第一个线程退出 p 方法,释放锁,第二个线程可以获取锁并退出等待方法。此时它检查 while 循环条件,发现 n 大于 0,这要归功于第一个线程,然后再次进入 wait 方法并回到它开始的地方。

请注意,notifyAll 通常很浪费,因为多个线程被唤醒但只有一个线程可以取得进展。

【讨论】:

    【解决方案2】:

    是否会在两个线程中读取代码直到方法结束? “同时”?

    没有,也没有。 notifyAll() 的工作原理是每个对象最终在从 wait() 醒来后都可以执行它们的代码。但是,一次只能执行一个线程。正因为如此,在一个线程通过调用r() 退出循环后,它会增加计数器,因此不会让另一个线程退出。因此,只有一个线程会执行到最后 - 而不是在两个线程中。

    如果我用这个资源启动了两个线程并且它们都在 wait() 并且我在资源中调用了方法 r() 会唤醒两个线程而不检查条件吗?

    当您的意思是“不检查条件”时,我假设您的意思是退出 while 循环。如上所示,这不是两个线程的情况。

    TL;DR: notifyAll() 一次只允许一个线程执行,但它们最终都会执行。

    来源:https://*.com/a/37046/10869762

    【讨论】: