如果一个人在谷歌上搜索“notify()和notifyAll()之间的区别”,那么会跳出很多解释(撇开javadoc段落)。这都归结于被唤醒的等待线程的数量:notify()中有一个,notifyAll()中有所有线程。
然而(如果我确实理解了这些方法之间的区别),只有一个线程总是被选择用于进一步的监视采集;第一种情况是VM选择的线程,第二种情况是系统线程调度程序选择的线程。程序员不知道它们的确切选择过程(在一般情况下)。
那么notify()和notifyAll()之间有什么有用的区别呢?我遗漏了什么吗?
我认为这取决于资源是如何生产和消费的。如果同时有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.
总结一下上面的详细解释,用我能想到的最简单的方式,这是由于JVM内置监控器的限制,1)在整个同步单元(块或对象)上获得,2)不区分正在等待/通知/关于的特定条件。
这意味着如果多个线程正在等待不同的条件,并且使用了notify(),所选择的线程可能不是在新满足的条件上取得进展的线程——导致该线程(以及其他当前仍在等待的能够满足条件的线程等)不能取得进展,最终饥饿或程序挂起。
相反,notifyAll()允许所有等待的线程最终重新获得锁并检查各自的条件,从而最终允许执行进程。
因此,notify()只有在任何等待线程被选中时保证允许进程,才可以安全地使用,当同一监视器中的所有线程只检查一个相同的条件时,通常可以满足这一点——在实际应用程序中相当罕见的情况。