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

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

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


当前回答

Notify()唤醒在同一对象上调用wait()的第一个线程。

notifyAll()唤醒在同一对象上调用wait()的所有线程。

优先级最高的线程将首先运行。

其他回答

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

摘自Java大师Joshua Bloch在Effective Java第二版中的文章:

“第69项:选择并发实用程序而不是等待和通知”。

这个答案是xagyg的优秀答案的图形重写和简化,包括eran的评论。

为什么要使用notifyAll,即使每个产品都是针对单个消费者的?

考虑生产者和消费者,简化如下。

制作人:

while (!empty) {
   wait() // on full
}
put()
notify()

消费者:

while (empty) {
   wait() // on empty
}
take()
notify()

假设2个生产者和2个消费者共享一个大小为1的缓冲区。下图描述了一个导致死锁的场景,如果所有线程都使用notifyAll,就可以避免死锁。

每个通知都被标记为被唤醒的线程。

在这里,所有的醒来都没有多大意义。 Wait notify和notifyall,所有这些都放在拥有对象的监视器之后。如果一个线程处于等待阶段,并且调用了notify,那么这个线程将占用该锁,此时没有其他线程可以占用该锁。所以并发访问根本不能发生。据我所知,只有在锁定对象后才能调用wait notify和notifyall。如果我错了,请指正。

虽然上面有一些可靠的答案,但我对我读到的困惑和误解的数量感到惊讶。这可能证明了应该尽可能多地使用java.util.concurrent,而不是尝试编写自己的坏并发代码。

回到问题:总结一下,目前的最佳实践是在所有情况下避免notify(),因为会出现丢失唤醒的问题。任何不理解这一点的人都不应该被允许编写关键任务并发代码。如果你担心羊群问题,实现一次唤醒一个线程的安全方法是:

为等待线程构建一个显式的等待队列; 让队列中的每个线程等待它的前一个线程; 完成后让每个线程调用notifyAll()。

或者你可以使用Java.util.concurrent。*,它们已经实现了这一点。