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

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

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


当前回答

总结一下上面的详细解释,用我能想到的最简单的方式,这是由于JVM内置监控器的限制,1)在整个同步单元(块或对象)上获得,2)不区分正在等待/通知/关于的特定条件。

这意味着如果多个线程正在等待不同的条件,并且使用了notify(),所选择的线程可能不是在新满足的条件上取得进展的线程——导致该线程(以及其他当前仍在等待的能够满足条件的线程等)不能取得进展,最终饥饿或程序挂起。

相反,notifyAll()允许所有等待的线程最终重新获得锁并检查各自的条件,从而最终允许执行进程。

因此,notify()只有在任何等待线程被选中时保证允许进程,才可以安全地使用,当同一监视器中的所有线程只检查一个相同的条件时,通常可以满足这一点——在实际应用程序中相当罕见的情况。

其他回答

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

我很惊讶居然没有人提到臭名昭著的“失醒”问题(谷歌it)。

基本上:

如果有多个线程在等待同一个条件, 可以让你从状态A转换到状态B的多个线程, 可以让你从状态B转换到状态A的多个线程(通常是与状态1相同的线程), 从状态A转换到状态B应该通知1中的线程。

然后,您应该使用notifyAll,除非您有可证明的保证,丢失的唤醒是不可能的。

一个常见的例子是并发FIFO队列,其中: 多个排队者(1。和3。)可以将队列从空转换为非空 多个退出队列器(2。上面)可以等待条件“队列不是空的” Empty ->非空应该通知脱队列者

您可以很容易地编写一个交叉操作,其中从一个空队列开始,2个入队者和2个出队者交互,1个入队者保持休眠状态。

这是一个可以与死锁问题相比较的问题。

有用的差异:

Use notify() if all your waiting threads are interchangeable (the order they wake up doesn't matter), or if you only ever have one waiting thread. A common example is a thread pool used to execute jobs from a queue--when a job is added, one of threads is notified to wake up, execute the next job and go back to sleep. Use notifyAll() for other cases where the waiting threads may have different purposes and should be able to run concurrently. An example is a maintenance operation on a shared resource, where multiple threads are waiting for the operation to complete before accessing the resource.

我想提一下《Java并发实践》中解释的内容:

第一点,是Notify还是NotifyAll?

It will be NotifyAll, and reason is that it will save from signall hijacking.

If two threads A and B are waiting on different condition predicates of same condition queue and notify is called, then it is upto JVM to which thread JVM will notify. Now if notify was meant for thread A and JVM notified thread B, then thread B will wake up and see that this notification is not useful so it will wait again. And Thread A will never come to know about this missed signal and someone hijacked it's notification. So, calling notifyAll will resolve this issue, but again it will have performance impact as it will notify all threads and all threads will compete for same lock and it will involve context switch and hence load on CPU. But we should care about performance only if it is behaving correctly, if it's behavior itself is not correct then performance is of no use.

这个问题可以通过使用jdk 5中提供的显式锁定Lock的Condition对象来解决,因为它为每个条件谓词提供了不同的等待。在这里,它将表现正确,不会有性能问题,因为它将调用信号,并确保只有一个线程正在等待该条件

这里有一个简单的解释:

您是正确的,无论使用notify()还是notifyAll(),直接结果都是恰好有另一个线程获得监视器并开始执行。(假设一些线程实际上在wait()上阻塞了这个对象,其他不相关的线程并没有占用所有可用的内核,等等。)

假设线程A、B和C正在等待这个对象,线程A得到监视器。区别在于当A释放监视器时发生了什么。如果你使用notify(),那么B和C仍然被阻塞在wait()中:它们不是在监视器上等待,而是在等待通知。当A释放监视器时,B和C仍然在那里等待notify()。

如果使用notifyAll(),则B和C都已进入“等待通知”状态,都在等待获取监视器。当A释放监视器时,B或C将获得它(假设没有其他线程竞争该监视器)并开始执行。