如果一个人在谷歌上搜索“notify()和notifyAll()之间的区别”,那么会跳出很多解释(撇开javadoc段落)。这都归结于被唤醒的等待线程的数量:notify()中有一个,notifyAll()中有所有线程。

然而(如果我确实理解了这些方法之间的区别),只有一个线程总是被选择用于进一步的监视采集;第一种情况是VM选择的线程,第二种情况是系统线程调度程序选择的线程。程序员不知道它们的确切选择过程(在一般情况下)。

那么notify()和notifyAll()之间有什么有用的区别呢?我遗漏了什么吗?


当前回答

看看@xagyg发布的代码。

假设两个不同的线程正在等待两个不同的条件: 第一个线程等待buf.size() != MAX_SIZE,第二个线程等待buf.size() != 0。

假设在某一点buf.size()不等于0。JVM调用notify()而不是notifyAll(),并且通知第一个线程(而不是第二个线程)。

第一个线程被唤醒,检查buf.size(),它可能返回MAX_SIZE,然后返回等待。第二个线程没有被唤醒,继续等待,不调用get()。

其他回答

我认为这取决于资源是如何生产和消费的。如果同时有5个工作对象,并且您有5个消费者对象,那么使用notifyAll()唤醒所有线程是有意义的,这样每个线程都可以处理1个工作对象。

如果您只有一个可用的工作对象,那么唤醒所有使用者对象来争夺这个对象有什么意义呢?第一个检查可用工作的线程将得到它,所有其他线程将检查并发现它们无事可做。

我在这里找到了一个很好的解释。简而言之:

The notify() method is generally used for resource pools, where there are an arbitrary number of "consumers" or "workers" that take resources, but when a resource is added to the pool, only one of the waiting consumers or workers can deal with it. The notifyAll() method is actually used in most other cases. Strictly, it is required to notify waiters of a condition that could allow multiple waiters to proceed. But this is often difficult to know. So as a general rule, if you have no particular logic for using notify(), then you should probably use notifyAll(), because it is often difficult to know exactly what threads will be waiting on a particular object and why.

等待队列和阻塞队列

您可以假设与每个锁对象关联的队列有两种类型。一个是阻塞队列,包含等待监控器锁的线程,另一个是等待队列,包含等待通知的线程。(线程调用Object.wait时将被放入等待队列)。

每次锁可用时,调度器从阻塞队列中选择一个线程执行。

当调用notify时,等待队列中只有一个线程被放入阻塞队列中争夺锁,而notifyAll将等待队列中的所有线程放入阻塞队列中。

现在你能看出区别了吗? 尽管在这两种情况下,只有一个线程被执行,但使用notifyAll,其他线程仍然得到一个要执行的更改(因为它们在阻塞队列中),即使它们未能争用锁。

一些指导原则

我基本上建议一直使用notifyAll,尽管可能会有一点性能损失。 仅在以下情况下使用notify:

任何被唤醒的线程都可以使程序继续运行。 性能很重要。

例如: @xagyg的回答给出了一个通知会导致死锁的例子。在他的例子中,生产者和消费者都与同一个锁对象相关。因此,当生产者调用notify时,可以通知生产者或消费者。但是,如果一个生产者被唤醒,它就不能使程序继续进行,因为缓冲区已经满了。因此发生了死锁。 有两种解决方法:

使用@xagyg建议的notifyALl。 使生产者和消费者关联不同的锁对象,并且生产者只能唤醒消费者,消费者只能唤醒生产者。在这种情况下,无论唤醒哪个消费者,它都可以消费缓冲区并使程序继续进行。

据我所知,以上所有答案都是正确的,所以我要告诉你一些其他的事情。对于生产代码,您确实应该使用java.util.concurrent中的类。在java的并发性方面,它们几乎没有不能为你做的事情。

Notify将只通知处于等待状态的一个线程,而Notify all将通知处于等待状态的所有线程,现在所有被通知的线程和所有被阻塞的线程都有资格获得锁,其中只有一个线程将获得锁,所有其他线程(包括之前处于等待状态的线程)将处于阻塞状态。

简短的总结:

总是更喜欢使用notifyAll()而不是notify(),除非你有一个大型并行应用程序,其中大量线程都在做同样的事情。

解释:

notify()[…醒来时还是单身 线程。因为notify()不允许指定线程 唤醒后,它只在大规模并行应用程序中有用 是指具有大量线程的程序,它们都执行类似的任务。 在这样的应用程序中,您不关心哪个线程被唤醒。

来源:https://docs.oracle.com/javase/tutorial/essential/concurrency/guardmeth.html

比较上述情况下的notify()和notifyAll():一个大型并行应用程序,其中线程在做相同的事情。如果在这种情况下调用notifyAll(), notifyAll()将导致大量线程的唤醒(即调度),其中许多线程是不必要的(因为实际上只有一个线程可以继续,即被授予wait()、notify()或notifyAll()对象监视的线程被调用),因此浪费计算资源。

因此,如果您的应用程序中没有大量线程并发地做同样的事情,请使用notifyAll()而不是notify()。为什么?因为,正如其他用户已经在这个论坛上回答的那样,notify()

唤醒正在此对象的监视器上等待的单个线程。[…] 选择是任意的,发生在自由裁量权 实现。

来源:Java SE8 API (https://docs.oracle.com/javase/8/docs/api/java/lang/Object.html#notify--)

Imagine you have a producer consumer application where consumers are ready (i.e. wait() ing) to consume, producers are ready (i.e. wait() ing) to produce and the queue of items (to be produced / consumed) is empty. In that case, notify() might wake up only consumers and never producers because the choice who is waken up is arbitrary. The producer consumer cycle wouldn't make any progress although producers and consumers are ready to produce and consume, respectively. Instead, a consumer is woken up (i.e. leaving the wait() status), doesn't take an item out of the queue because it's empty, and notify() s another consumer to proceed.

In contrast, notifyAll() awakens both producers and consumers. The choice who is scheduled depends on the scheduler. Of course, depending on the scheduler's implementation, the scheduler might also only schedule consumers (e.g. if you assign consumer threads a very high priority). However, the assumption here is that the danger of the scheduler scheduling only consumers is lower than the danger of the JVM only waking up consumers because any reasonably implemented scheduler doesn't make just arbitrary decisions. Rather, most scheduler implementations make at least some effort to prevent starvation.