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

看到各种与锁定相关的问题,并且(几乎)总是找到“由于虚假唤醒而循环”术语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();
        }
    }
}

我能做些什么来虚假地唤醒它,而不必永远等待随机事件?await


答案 1

维基百科上关于虚假唤醒的文章有这样的花絮:

Linux 中的函数是使用系统调用实现的。Linux 上的每个阻塞系统调用都会随着进程收到信号而突然返回。... 无法重新启动等待,因为它可能会在系统调用之外的短时间内错过真正的唤醒。只有调用方检查不变量才能避免此争用情况。因此,POSIX信号将产生杂散唤醒。pthread_cond_wait()futexEINTRpthread_cond_wait()futex

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

我买它。这是一种更容易吞咽的药丸,而不是通常给出的模糊的“这是为了性能”的原因。


答案 2

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