|
|
1
237
这是不正确的。
简单地说,这取决于线程等待通知的原因。您想告诉其中一个等待线程发生了什么,还是想同时告诉所有线程? 在某些情况下,一旦等待结束,所有等待线程都可以采取有用的操作。例如,一组等待某个任务完成的线程;一旦任务完成,所有等待的线程都可以继续其业务。在这种情况下,你会使用 通知() 同时唤醒所有等待的线程。 另一种情况,例如互斥锁,只有一个等待线程在收到通知后可以做一些有用的事情(在本例中,获取锁)。在这种情况下,你宁愿使用 通知() . 正确实施,你 能够 使用 通知() 在这种情况下也一样,但是您将不必要地唤醒那些无论如何都不能做任何事情的线程。 在许多情况下,等待条件的代码将被编写为一个循环:
这样,如果
|
|
|
2
306
显然,
仔细阅读并理解。如果您有任何问题,请给我发一封电子邮件。
看看生产者/消费者(假设是一个具有两种方法的ProducerConsumer类)。它坏了(因为它使用
首先, 为什么我们需要一个关于等待的while循环?
我们需要一个
使用者1(c1)进入同步块,缓冲区为空,因此c1被放入等待集(通过
现在C1和C2正试图获取同步锁。其中一个(非确定性地)被选择并进入方法,另一个被阻塞(不是等待而是阻塞,试图获取方法的锁)。假设C2先拿到锁。c1仍在阻塞(试图在x处获取锁)。C2完成方法并释放锁。现在,C1获得了锁。你猜怎么着,幸运的是我们有一个
现在, 好吧,现在为什么我们需要通知所有人?
在上面的生产者/消费者示例中,我们似乎可以摆脱
但是,这还不够好,我们需要使用
假设我们有一个大小为1的缓冲区(以使示例易于理解)。以下步骤导致我们陷入僵局。请注意,每当一个线程被通知唤醒时,它可以被JVM非确定性地选择——也就是说,任何等待的线程都可以被唤醒。还要注意的是,当多个线程在一个方法的入口阻塞时(即试图获取一个锁),获取的顺序可能是不确定的。还请记住,线程在任何时候都只能在其中一个方法中-同步方法只允许一个线程执行(即持有类中任何(同步)方法的锁)。如果发生以下事件序列-死锁结果:
步骤1:
步骤2:
步骤3:
步骤4:
步骤5:
步骤6:
步骤7:
解决方案:更换
|
|
|
3
37
有用的区别:
|
|
|
4
18
我认为这取决于如何生产和消耗资源。如果同时有5个工作对象可用,并且您有5个使用者对象,那么使用notifyall()唤醒所有线程是有意义的,这样每个线程都可以处理1个工作对象。 如果您只有一个可用的工作对象,那么唤醒所有的消费者对象来争夺那个对象有什么意义呢?第一个检查可用工作的线程将得到它,所有其他线程将检查并发现它们与之无关。 我找到了一个 great explanation here . 简而言之:
|
|
|
5
11
注意,对于并发实用程序,您还可以在
DougLea提出了一个有趣的观点
famous book
如果是
|
|
|
6
10
从Joshua Bloch,Java大师在有效的Java第二版中: “第69项:首选并发实用程序等待并通知”。 |
|
|
7
9
下面是一个例子。运行它。然后将notifyAll()中的一个更改为notify(),并查看会发生什么。 ProducerConsumerExample类
Dropbox类
消费阶层
生产者阶级
|
|
|
8
8
简短摘要: 总是喜欢 通知() 结束 通知() 除非您有一个大规模并行应用程序,其中大量线程都执行相同的操作。 说明:
来源: https://docs.oracle.com/javase/tutorial/essential/concurrency/guardmeth.html 比较 通知() 具有 通知() 在上面描述的情况下:线程执行相同操作的大规模并行应用程序。如果你打电话 通知() 在那种情况下, 通知() 将导致大量线程的唤醒(即调度),其中许多线程是不必要的(因为实际上只有一个线程可以继续,即将被授予对象监视器的线程) WAIT() , 通知() 或 通知() 因此浪费了计算资源。 因此,如果您没有一个应用程序,其中有大量线程同时执行相同的操作,那么最好 通知() 结束 通知() . 为什么?因为,正如其他用户在本论坛中已经回答的那样, 通知()
来源:Java SE8API https://docs.oracle.com/javase/8/docs/api/java/lang/Object.html#notify-- ) 假设您有一个生产者-消费者应用程序,其中消费者已经准备好了(即 WAIT() 为了消费,生产者准备好了(即 WAIT() 生产和项目队列(待生产/消费)为空。在那种情况下, 通知() 可能只会唤醒消费者,而不会唤醒生产者,因为唤醒者的选择是 任意的 . 虽然生产者和消费者已经准备好分别生产和消费,但生产者-消费者循环不会取得任何进展。相反,消费者被唤醒(即离开 WAIT() 状态),不会将项目从队列中取出,因为它是空的,并且 通知() 另一个消费者继续。 相反, 通知() 唤醒生产者和消费者。计划的对象取决于计划程序。当然,根据调度程序的实现,调度程序也可能只调度使用者(例如,如果将使用者线程分配为非常高的优先级)。然而,这里的假设是,调度程序只调度使用者的危险性低于JVM只唤醒使用者的危险性,因为任何合理实现的调度程序都不会使 任意的 决定。相反,大多数调度器实现至少做出了一些努力来防止饥饿。 |
|
|
9
5
我很惊讶没有人提到臭名昭著的“迷路唤醒”问题(google it)。 基本上:
那么你应该使用notifyall,除非你有可证明的保证,失去唤醒是不可能的。 一个常见的例子是并发FIFO队列,其中: 多个排队者(1.3。上面)可以将队列从空队列转换为非空队列 多个出局者(2.上面)可以等待条件“队列不为空” 空->非空应通知出局者 您可以轻松地编写一个交叉操作,其中,从空队列开始,2个排队者和2个排队者交互,1个排队者将保持睡眠。 这是一个可以与死锁问题相提并论的问题。 |
|
|
10
5
我希望这能消除一些疑问。 notify() :notify()方法唤醒一个等待的线程 对于锁(在该锁上调用wait()的第一个线程)。 notifyAll() :notifyall()方法唤醒所有等待锁的线程;jvm从等待锁的线程列表中选择一个线程并唤醒 那根线。 如果是单螺纹 等待锁定时,notify()和notifyall()之间没有显著差异。但是,当有多个线程在等待锁时,在notify()和notifyall()中,实际唤醒的线程是 在合资公司的控制下 而且您不能通过编程控制唤醒特定线程。 乍一看,只调用notify()来唤醒一个线程似乎是个好主意;似乎不需要唤醒所有线程。但是,问题在于 notify()是唤醒的线程可能不是合适的线程 被唤醒(线程可能正在等待其他条件,或者该条件仍然不满足于该线程等)。 在那种情况下 ,notify()可能会丢失,没有其他线程可能会唤醒,导致死锁类型(通知丢失,所有其他线程都在等待notificationforer)。 为了避免这个问题 ,当有多个线程在等待锁(或在多个条件下完成等待)时,最好调用notifyall()。notifyAll()方法会唤醒所有线程,因此效率不高。然而,在实际应用中,这种性能损失是可以忽略的。 |
|
|
11
4
|
|
|
12
4
据我所知,以上所有答案都是正确的,所以我要告诉你一些其他的事情。对于生产代码,您确实应该使用java.util.concurrent中的类。在Java的并发性方面,它们几乎不能为您做什么。 |
|
|
13
4
|
|
|
14
3
考虑从多个并行线程执行以下代码:
它可以通过使用
在这种情况下,如果您有大量线程,或者等待循环条件的评估成本很高,
使用
最后,重要的是要了解
|
|
|
15
3
下面是一个简单的解释: 正确的做法是,无论使用notify()还是notifyall(),立即的结果是,只有另外一个线程会获取监视器并开始执行。(假设在wait()上有一些线程被阻塞了,其他无关的线程并没有吸收所有可用的核心,等等)影响会在稍后发生。 假设线程A、B和C正在等待这个对象,线程A得到监视器。区别在于一旦a释放监视器会发生什么。如果您使用notify(),那么b和c在wait()中仍然被阻塞:它们没有在监视器上等待,而是在等待通知。当A释放监视器时,B和C仍将坐在那里,等待通知()。 如果您使用notifyall(),那么b和c都已经超过了“等待通知”状态,并且都在等待获取监视器。当a释放监视器时,b或c将获取它(假设没有其他线程竞争该监视器)并开始执行。 |
|
|
16
3
线程有三种状态。
现在,当调用notify()时,jvm会选择一个线程,并将它们移动到阻塞状态,从而移动到运行状态,因为监视器对象没有竞争。 当调用notifyall()时,jvm将挑选所有线程并将它们全部移动到阻塞状态。所有这些线程都将优先获得对象的锁,首先能够获取监视器的线程将首先进入运行状态,依此类推。 |
|
|
17
3
取自 blog 有效Java:
所以,我所理解的是(从前面提到的博客中,“yann tm”在 accepted answer 和爪哇 docs ):
|
|
18
2
看看@xagyg发布的代码。
假设两个不同的线程在等待两个不同的条件:
假设在某个时刻
第一根线被唤醒,检查
|
|
|
19
1
最高优先级的线程将首先运行。 |
|
|
20
1
我想提一下在实践中Java并发解释了什么: 第一点,是通知还是通知全部?
这个问题可以通过使用JDK5中提供的显式锁的条件对象来解决,因为它为每个条件谓词提供了不同的等待。在这里,它将正确运行,不会出现性能问题,因为它将调用信号,并确保只有一个线程在等待该条件。 |
|
|
21
1
notify将只通知一个处于等待状态的线程,notify all将立即通知处于等待状态的所有线程,所有通知的线程和所有被阻止的线程都符合锁定条件,其中只有一个线程将获得锁定,所有其他线程(包括之前处于等待状态的线程)将处于被阻止状态。 |
|
|
22
1
|
|
|
23
0
综上所述,以我能想到的最简单的方式,这是由于JVM内置监视器的局限性,它1)在整个同步单元(块或对象)上获取,2)不区分等待/通知/关于的特定条件。 这意味着,如果多个线程在不同的条件下等待,并且使用了notify(),则选定的线程可能不是在新满足的条件下会取得进展的线程,从而导致该线程(以及其他当前仍在等待的、能够满足该条件的线程等)无法取得进展,并且ev经常挨饿或程序挂断。 相反,notifyall()允许所有等待线程最终重新获取锁并检查其各自的条件,从而最终允许进行进程。 因此,只有在保证任何等待线程允许进行进程的情况下,才可以安全地使用notify(),通常,当同一监视器中的所有线程仅检查一个和相同的条件时都会满足这一要求—在现实世界的应用程序中这是非常罕见的情况。 |
|
|
24
0
当您调用“object”(期望获得对象锁)的wait()时,intern将释放该对象上的锁,帮助其他线程锁定该“object”,在这种情况下,将有一个以上的线程等待“resource/object”(考虑到其他线程也对相同的上述对象发出了等待)接下来会有一个线程填充资源/对象并调用notify/notifyall)。 在这里,当您发出同一对象的通知(来自进程/代码的同一侧/另一侧)时,这将释放一个被阻塞并等待的单线程(不是所有等待的线程——这个释放的线程将由JVM线程调度程序选择,并且对象上的所有锁获取进程与常规线程相同)。 如果只有一个线程将共享/处理此对象,那么可以在wait notify实现中单独使用notify()方法。 如果您处于一个以上的线程根据您的业务逻辑对资源/对象进行读写的情况,那么您应该使用notifyall()。现在,我正在查看当我们对一个对象发出notify()时,JVM是如何准确地标识和破坏等待线程的… |
|
|
25
0
虽然上面有一些可靠的答案,但我对我读到的困惑和误解的数量感到惊讶。这可能证明了这样一种观点,即应该尽可能多地使用java.util.concurrent,而不是试图编写自己的中断的并发代码。 回到问题:总结一下,今天的最佳实践是避免在所有情况下由于丢失唤醒问题而出现notify()。任何不理解这一点的人都不应该被允许编写关键任务并发代码。如果您担心放牧问题,一次唤醒一个线程的一个安全方法是: 1。为等待线程建立一个显式的等待队列; 2。让队列中的每个线程等待它的前一个线程; 三。完成后让每个线程调用notifyall()。 或者可以使用java. UTI.Orth.*,它已经实现了这一点。 |
|
|
26
-2
在这里,醒来没有什么意义。 等待notify和notify all,所有这些都在拥有对象监视器之后放置。如果一个线程处于等待阶段并调用了notify,那么这个线程将占用锁,此时没有其他线程可以占用该锁。所以不能同时访问。据我所知,任何等待通知和notifyall调用都只能在获取对象的锁之后进行。如果我错了就纠正我。 |
|
|
user29759326 · 如何返回递归函数中的最后一个值? 9 月前 |
|
|
malife89 · 将java中的字符串读取为正确的日期格式 9 月前 |
|
|
Tim · 在java中,有没有更快的方法将字节数组写入文件? 9 月前 |
|
|
rudraraj · java中未声明最终变量 9 月前 |
|
|
Bala Ji · 以下BFS的实施效率如何? 9 月前 |