我被教导说,并发条件并不一定需要在循环中编写,而不是oracle doc所说的

keo*_*ont 2 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
}
Run Code Online (Sandbox Code Playgroud)

完全没问题,因为他说虚假唤醒不会发生,所以不需要循环:

lock.lock()
while (p>q) {
    lock.newCondition().await
}
Run Code Online (Sandbox Code Playgroud)

我更有可能把事情和理解文件和我的老师的方式混合在一起,但是我确实花了一些时间试图理解为什么每件事,并且不能带有我自己的"答案",我要么相信一个或另一个(不重要,它是纯粹的我想要学习).

我的老师确实花时间告诉我们如何解释Java中的并发性是非常愚蠢的,但我也没有选择它,所以就是这样.

Gra*_*ray 5

完全没问题,因为他说虚假唤醒不会发生,它不需要循环:

你的老师错了两个原因:

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

    根本问题是某些本机线程实现可能会选择唤醒与应用程序关联的所有条件,而不是通知的特定条件.这在javadocs中有详细记载Object.wait():

    与在一个参数版本中一样,中断和虚假唤醒是可能的,并且此方法应始终在循环中使用:

    以下是具有此限制的体系结构的一个示例.我会引用这篇有趣的博客文章:

    在内部,wait实现为对'futex'系统调用的调用.当进程收到信号时,Linux上的每个阻塞系统调用都会突然返回 - 因为来自内核调用的调用信号处理程序很棘手.如果信号处理程序调用其他系统函数怎么办?一个新的信号到来了吗?对于进程来说,很容易耗尽内核堆栈.正是因为每次系统调用都可以被中断,当glibc调用任何阻塞函数时,比如'read',它会在循环中执行,如果'read'返回EINTR,则再次调用'read'.

  2. 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();
Run Code Online (Sandbox Code Playgroud)

然后,如果生产者向队列添加内容并调用signal()线程A从WAIT状态移动到BLOCKED状态以获得锁本身.但它可能是已经在等待的线程B的后面.一旦释放锁,线程B就会使元素出队,而不是线程A.当线程A有机会出队时,队列为空.如果没有while循环,您可以通过尝试从空队列中出列来获得越界异常或其他问题.

有关比赛的更多详细信息,请参阅我的链接.