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

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

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


当前回答

notify()让您编写比notifyAll()更有效的代码。

考虑下面这段从多个并行线程执行的代码:

synchronized(this) {
    while(busy) // a loop is necessary here
        wait();
    busy = true;
}
...
synchronized(this) {
    busy = false;
    notifyAll();
}

可以通过使用notify()来提高效率:

synchronized(this) {
    if(busy)   // replaced the loop with a condition which is evaluated only once
        wait();
    busy = true;
}
...
synchronized(this) {
    busy = false;
    notify();
}

在有大量线程的情况下,或者如果等待循环条件的计算成本很高,notify()将比notifyAll()快得多。例如,如果你有1000个线程,那么999个线程将在第一个notifyAll()之后被唤醒和评估,然后是998,然后是997,依此类推。相反,使用notify()解决方案,只会唤醒一个线程。

使用notifyAll()当你需要选择哪个线程将做下一步工作:

synchronized(this) {
    while(idx != last+1)  // wait until it's my turn
        wait();
}
...
synchronized(this) {
    last = idx;
    notifyAll();
}

Finally, it's important to understand that in case of notifyAll(), the code inside synchronized blocks that have been awakened will be executed sequentially, not all at once. Let's say there are three threads waiting in the above example, and the fourth thread calls notifyAll(). All three threads will be awakened but only one will start execution and check the condition of the while loop. If the condition is true, it will call wait() again, and only then the second thread will start executing and will check its while loop condition, and so on.

其他回答

这里有一个简单的解释:

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

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

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

notify()将唤醒一个线程,而notifyAll()将唤醒所有线程。据我所知,没有中间立场。但是如果你不确定notify()会对你的线程做什么,使用notifyAll()。每次都很灵验。

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

基本上:

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

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

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

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

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

我认为这取决于资源是如何生产和消费的。如果同时有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.

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