如果一个人在谷歌上搜索“notify()和notifyAll()之间的区别”,那么会跳出很多解释(撇开javadoc段落)。这都归结于被唤醒的等待线程的数量:notify()中有一个,notifyAll()中有所有线程。
然而(如果我确实理解了这些方法之间的区别),只有一个线程总是被选择用于进一步的监视采集;第一种情况是VM选择的线程,第二种情况是系统线程调度程序选择的线程。程序员不知道它们的确切选择过程(在一般情况下)。
那么notify()和notifyAll()之间有什么有用的区别呢?我遗漏了什么吗?
摘自Effective Java博客:
The notifyAll method should generally be used in preference to notify.
If notify is used, great care must be taken to ensure liveness.
所以,我的理解是(从前面提到的博客,“Yann TM”对公认答案和Java文档的评论):
notify() : JVM awakens one of the waiting threads on this object. Thread selection is made arbitrarily without fairness. So same thread can be awakened again and again. So system's state changes but no real progress is made. Thus creating a livelock.
notifyAll() : JVM awakens all threads and then all threads race for the lock on this object. Now, CPU scheduler selects a thread which acquires lock on this object. This selection process would be much better than selection by JVM. Thus, ensuring liveness.
但是(如果我正确理解了这些方法之间的区别),总是只选择一个线程进行进一步的监视采集。
这是不对的。o.notifyAll()唤醒在o.wait()调用中阻塞的所有线程。线程只允许一个接一个地从o.wait()返回,但它们将轮流返回。
简单地说,这取决于线程等待通知的原因。您是想告诉其中一个等待线程发生了什么,还是想同时告诉所有等待线程?
在某些情况下,所有等待线程在等待结束后都可以采取有用的操作。一个例子是一组等待某个任务完成的线程;一旦任务完成,所有等待的线程都可以继续它们的业务。在这种情况下,您可以使用notifyAll()同时唤醒所有等待的线程。
另一种情况,例如互斥锁,只有一个等待线程在被通知后可以做一些有用的事情(在这种情况下获得锁)。在这种情况下,您更愿意使用notify()。如果实现得当,您也可以在这种情况下使用notifyAll(),但是您将不必要地唤醒无法做任何事情的线程。
在很多情况下,等待条件的代码会被写成循环:
synchronized(o) {
while (! IsConditionTrue()) {
o.wait();
}
DoSomethingThatOnlyMakesSenseWhenConditionIsTrue_and_MaybeMakeConditionFalseAgain();
}
这样,如果一个o.notifyAll()调用唤醒了多个等待线程,并且第一个从o.wait() make返回的线程将条件保持在false状态,那么其他被唤醒的线程将返回等待状态。