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

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

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


当前回答

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

其他回答

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

简短的总结:

总是更喜欢使用notifyAll()而不是notify(),除非你有一个大型并行应用程序,其中大量线程都在做同样的事情。

解释:

notify()[…醒来时还是单身 线程。因为notify()不允许指定线程 唤醒后,它只在大规模并行应用程序中有用 是指具有大量线程的程序,它们都执行类似的任务。 在这样的应用程序中,您不关心哪个线程被唤醒。

来源:https://docs.oracle.com/javase/tutorial/essential/concurrency/guardmeth.html

比较上述情况下的notify()和notifyAll():一个大型并行应用程序,其中线程在做相同的事情。如果在这种情况下调用notifyAll(), notifyAll()将导致大量线程的唤醒(即调度),其中许多线程是不必要的(因为实际上只有一个线程可以继续,即被授予wait()、notify()或notifyAll()对象监视的线程被调用),因此浪费计算资源。

因此,如果您的应用程序中没有大量线程并发地做同样的事情,请使用notifyAll()而不是notify()。为什么?因为,正如其他用户已经在这个论坛上回答的那样,notify()

唤醒正在此对象的监视器上等待的单个线程。[…] 选择是任意的,发生在自由裁量权 实现。

来源:Java SE8 API (https://docs.oracle.com/javase/8/docs/api/java/lang/Object.html#notify--)

Imagine you have a producer consumer application where consumers are ready (i.e. wait() ing) to consume, producers are ready (i.e. wait() ing) to produce and the queue of items (to be produced / consumed) is empty. In that case, notify() might wake up only consumers and never producers because the choice who is waken up is arbitrary. The producer consumer cycle wouldn't make any progress although producers and consumers are ready to produce and consume, respectively. Instead, a consumer is woken up (i.e. leaving the wait() status), doesn't take an item out of the queue because it's empty, and notify() s another consumer to proceed.

In contrast, notifyAll() awakens both producers and consumers. The choice who is scheduled depends on the scheduler. Of course, depending on the scheduler's implementation, the scheduler might also only schedule consumers (e.g. if you assign consumer threads a very high priority). However, the assumption here is that the danger of the scheduler scheduling only consumers is lower than the danger of the JVM only waking up consumers because any reasonably implemented scheduler doesn't make just arbitrary decisions. Rather, most scheduler implementations make at least some effort to prevent starvation.

当你调用wait()的“对象”(期望对象锁)、实习生这将释放锁,物体和帮助的其他线程锁在这个“对象”,在这种情况下,将会有超过1线程等待“资源/对象”(考虑到其他线程也发布了等待上面相同的对象,将会有一个线程的方式填补资源/对象并调用通知/ notifyAll)。

在这里,当您(从进程/代码的同一/另一端)发出同一对象的通知时,这将释放一个阻塞和等待的单个线程(不是所有等待的线程——这个释放的线程将由JVM thread Scheduler挑选,对象上的所有锁获取进程与常规进程相同)。

如果只有一个线程共享/处理这个对象,那么可以在wait-notify实现中单独使用notify()方法。

如果您处于基于业务逻辑的多个线程对资源/对象进行读写的情况,那么您应该使用notifyAll()

现在我正在寻找JVM是如何识别和打破等待线程时,我们发出通知()在一个对象…

但是(如果我正确理解了这些方法之间的区别),总是只选择一个线程进行进一步的监视采集。

这是不对的。o.notifyAll()唤醒在o.wait()调用中阻塞的所有线程。线程只允许一个接一个地从o.wait()返回,但它们将轮流返回。


简单地说,这取决于线程等待通知的原因。您是想告诉其中一个等待线程发生了什么,还是想同时告诉所有等待线程?

在某些情况下,所有等待线程在等待结束后都可以采取有用的操作。一个例子是一组等待某个任务完成的线程;一旦任务完成,所有等待的线程都可以继续它们的业务。在这种情况下,您可以使用notifyAll()同时唤醒所有等待的线程。

另一种情况,例如互斥锁,只有一个等待线程在被通知后可以做一些有用的事情(在这种情况下获得锁)。在这种情况下,您更愿意使用notify()。如果实现得当,您也可以在这种情况下使用notifyAll(),但是您将不必要地唤醒无法做任何事情的线程。


在很多情况下,等待条件的代码会被写成循环:

synchronized(o) {
    while (! IsConditionTrue()) {
        o.wait();
    }
    DoSomethingThatOnlyMakesSenseWhenConditionIsTrue_and_MaybeMakeConditionFalseAgain();
}

这样,如果一个o.notifyAll()调用唤醒了多个等待线程,并且第一个从o.wait() make返回的线程将条件保持在false状态,那么其他被唤醒的线程将返回等待状态。