java - 我被教导说并发条件不一定需要写在循环中,这与 oracle 文档所说的相反

标签 java eclipse concurrency

所以基本上我正在学习更严肃的并发性(研究事物的实际工作方式,而不是在需要时使用随机的东西)。

我的教授,当我问他这个问题时,他告诉我他和他的同事无法重现虚假的唤醒,并且相信那行是没有删除的旧行(就像,它在那里, 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:

你的老师错了有两个原因:

  1. 虚假唤醒确实发生了。它可能不会发生在他们测试的架构上,但如果您不考虑它,当您将应用程序移动到不同的硬件或不同的操作系统版本时,您会看到问题。也可能是在异常内核事件期间偶尔会发生虚假中断,例如在错误的时间传递信号。同样,您的应用程序可能在测试中运行良好,但当您将其以高得多的负载移至生产环境时,异常事件的频率可能会增加...

    潜在的问题是,某些 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.

  2. while 循环对于防止竞争条件也非常重要——尤其是在多线程生产者/消费者模型中。如果您有多个线程正在从一个队列中消费(例如),队列中有项目的通知可能会唤醒一个线程,但是当它能够获得锁时,另一个线程已经将该项目从队列中取出。

    这在我的页面上有很好的记录,这里有一个示例程序,演示了不使用 while 的竞争条件。

    Producer Consumer Thread Race Conditions

在您的示例中,线程 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/

相关文章:

java - 如何使用 spring boot 2 和千分尺测量服务方法

java - Android Studio : Showing web page that requires login in WebView

java - 使用 LibGDX 加载 .png 文件时出现问题

android - 将Opencv链接到我自己的android项目

python - 如何使用 Eclipse 和 PyDev 设置远程调试

Java 并发 : Are "get(Key) for HashMap and ConcurrentHashMap equal in performance?

java - HibernateTemplate 和 Spring @Transactional 如何协同工作?

c++ - Armadillo 与 Eclipse

testing - docker-compose 与孤立的服务集一起运行?

java - 同时刷新缓存