所以基本上我正在学习更严肃的并发性(研究事物的实际工作方式,而不是在需要时使用随机的东西)。
我的教授,当我问他这个问题时,他告诉我他和他的同事无法重现虚假的唤醒,并且相信那行是没有删除的旧行(就像,它在那里, java 变得“更好”了,它不再需要了,那条线还在),但事实并非如此。
链接:
http://docs.oracle.com/javase/7/docs/api/java/util/concurrent/locks/Condition.html
它就在点的正下方:
实现注意事项
在他看来,情况看起来有点像这样:
lock.lock()
if (p>q) {
lock.newCondition().await
}
完全没问题,因为他说虚假唤醒不会发生,所以不需要循环:
lock.lock()
while (p>q) {
lock.newCondition().await
}
我很可能会混淆事物并以错误的方式理解医生和我的老师,但我确实花了一些时间试图理解每件事的原因,但我无法给出自己的“答案”,我要么相信一个,要么相信另一个(这并不重要,这纯粹是我想学习)。
我的老师确实花时间告诉我们如何在 Java 中解释并发,这很愚蠢,但我也没有选择它,所以就是这样。
最佳答案
Would be perfectly fine, since he says a spurious wake up can not happen, it wouldnt be needed a loop:
你的老师错了有两个原因:
虚假唤醒确实发生了。它可能不会发生在他们测试的架构上,但如果您不考虑它,当您将应用程序移动到不同的硬件或不同的操作系统版本时,您会看到问题。也可能是在异常内核事件期间偶尔会发生虚假中断,例如在错误的时间传递信号。同样,您的应用程序可能在测试中运行良好,但当您将其以高得多的负载移至生产环境时,异常事件的频率可能会增加...
潜在的问题是,某些 native 线程实现可能会选择唤醒与应用程序关联的所有条件,而不是被通知的特定条件。这在
Object.wait()
的 javadocs 中有详细记录:As in the one argument version, interrupts and spurious wakeups are possible, and this method should always be used in a loop:
这是一个具有此限制的架构示例。我会引用 this interesting blog entry :
Internally, wait is implemented as a call to the 'futex' system call. Each blocking system call on Linux returns abruptly when the process receives a signal -- because calling signal handler from kernel call is tricky. What if the signal handler calls some other system function? And a new signal arrives? It's easy to run out of kernel stack for a process. Exactly because each system call can be interrupted, when glibc calls any blocking function, like 'read', it does it in a loop, and if 'read' returns EINTR, calls 'read' again.
while
循环对于防止竞争条件也非常重要——尤其是在多线程生产者/消费者模型中。如果您有多个线程正在从一个队列中消费(例如),队列中有项目的通知可能会唤醒一个线程,但是当它能够获得锁时,另一个线程已经将该项目从队列中取出。这在我的页面上有很好的记录,这里有一个示例程序,演示了不使用
while
的竞争条件。
在您的示例中,线程 A 可能正在等待 await()
,而另一个线程 B 可能正在等待获取 lock()
。线程 C 有锁并正在添加到队列中。
// B is here waiting for the lock
lock.lock()
while (p > q) {
// A is here waiting for the signal
lock.newCondition().await();
}
// dequeue
lock.unlock();
然后,如果生产者向队列中添加一些东西并调用 signal()
,则线程 A 从 WAIT
状态移动到 BLOCKED
状态获取锁本身。但它可能在已经等待的线程 B 后面。一旦锁被释放,线程 B 将元素出列,而不是线程 A。当线程 A 有机会出列时,队列为空。如果没有 while 循环,您可能会因尝试从空队列中出队而遇到越界异常或其他问题。
查看我的链接以获取更明确的比赛细节。
关于java - 我被教导说并发条件不一定需要写在循环中,这与 oracle 文档所说的相反,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/20507330/