Java中的虚假唤醒真的发生了吗?

aka*_*okd 203 java multithreading locking spurious-wakeup

看到各种锁定相关的问题和(几乎)总是找到'循环因为虚假的唤醒'术语1我想知道,有没有人经历过这样的唤醒(假设一个像样的硬件/软件环境)?

我知道"虚假"一词意味着没有明显的理由,但这种事件的原因是什么?

(1注意:我不是在质疑循环练习.)

编辑:一个帮助问题(对于那些喜欢代码示例的人):

如果我有以下程序,我运行它:

public class Spurious {
    public static void main(String[] args) {
        Lock lock = new ReentrantLock();
        Condition cond = lock.newCondition();
        lock.lock();
        try {
            try {
                cond.await();
                System.out.println("Spurious wakeup!");
            } catch (InterruptedException ex) {
                System.out.println("Just a regular interrupt.");
            }
        } finally {
            lock.unlock();
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

await如果不等待随机事件,我该怎么做才能虚假地唤醒它?

Joh*_*ica 198

关于虚假唤醒的维基百科文章有这样一个小说:

pthread_cond_wait()Linux中的功能是使用futex系统调用实现的.EINTR当进程收到信号时,Linux上的每个阻塞系统调用都会突然返回.... pthread_cond_wait()无法重新启动等待,因为它可能会在futex系统调用之外的一小段时间内错过真正的唤醒.只有呼叫者检查不变量才能避免这种竞争条件.因此,POSIX信号将产生虚假唤醒.

简介:如果发出一个Linux进程的信号,它的等待线程将各自享受一个不错的,热的虚假唤醒.

我买它.这是一个比通常模糊的"性能"原因更容易吞下的药丸.

  • 这里有更好的解释:http://stackoverflow.com/questions/1461913/does-c-monitor-wait-suffer-from-spurious-wakeups/1461956#1461956 (13认同)
  • 对于Unix派生系统中的所有阻塞系统调用,此EINTR解除阻塞都是正确的.这使内核更简单,但应用程序员买了负担. (3认同)
  • 我以为 pthread_cond_wait() 和朋友不能返回 EINTR,但如果虚假唤醒则返回零?来自:http://pubs.opengroup.org/onlinepubs/7908799/xsh/pthread_cond_wait.html “这些函数不会返回 [EINTR] 的错误代码。” (2认同)
  • @jgubby那是对的.底层的`futex()`调用返回`EINTR`,但该返回值不会冒泡到下一级.因此,pthread调用者必须检查不变量.他们所说的是,当`pthread_cond_wait()`返回时,你必须再次检查你的循环条件(不变),因为等待可能已被虚假地唤醒了.在系统调用期间接收信号是一个可能的原因,但它不是唯一的原因. (2认同)

Mr.*_*rdy 22

我有一个展示这种行为的生产系统.线程等待队列中有消息的信号.在繁忙时期,高达20%的唤醒是虚假的(即当它唤醒时,队列中没有任何内容).该线程是消息的唯一消费者.它运行在Linux SLES-10 8处理器盒上,使用GCC 4.1.2构建.这些消息来自外部源并且是异步处理的,因为如果我的系统没有足够快地读取它们就会出现问题.


Ani*_*kur 13

回答titile中的问题 - 是的!它确实发生了.尽管维基文章提到了一个关于虚假唤醒的大量信息,但我遇到的一个很好的解释如下 -

想一想......就像任何代码一样,线程调度程序可能会因底层硬件/软件中发生异常而暂时停电.当然,应该注意尽可能少地发生这种情况,但是由于没有100%强大的软件这样的事情,所以假设这种情况可能发生并且在调度程序检测到这种情况时要注意优雅恢复是合理的(例如,通过观察失踪的心跳).

现在,调度程序如何恢复,考虑到在停电期间它可能会错过一些旨在通知等待线程的信号?如果调度程序什么都不做,那么提到的"不幸"线程就会挂起,永远等待 - 为了避免这种情况,调度程序只会向所有等待的线程发送一个信号.

这使得有必要建立一个"合同",即无需理由就可以通知等待线程.确切地说,有一个原因 - 调度程序停电 - 但由于线程被设计(有充分理由)无法调度内部实现细节,因此这个原因可能更好地表现为"虚假".

我正在从Source阅读这个答案,并认为它足够合理.还看了

Java中的虚假唤醒以及如何避免它们.

PS:以上链接是我的个人博客,其中包含有关虚假唤醒的其他详细信息.


oxb*_*kes 9

Cameron Purdy不久前写了一篇关于受到虚假唤醒问题打击的博客文章.是的,它发生了

我猜它是在规范中(作为一种可能性),因为Java部署的某些平台的局限性?虽然我可能错了!


Ren*_*neS 8

只是添加这个.是的,它发生了,我花了三天时间在24核计算机(JDK 6)上搜索多线程问题的原因.10次​​执行中有4次没有任何模式.这从未发生在2核或8核上.

研究了一些在线资料,这不是Java问题,而是一种罕见但预期的行为.