2009-06-26 130 views
176

看到各种锁定相关的问题,(几乎)总是查找术语我不知道“因为虚假唤醒的循环”的,有没有人经历过这样一种唤醒(假设例如一个体面的硬件/软件环境)?真的发生虚假唤醒吗?

我知道术语“伪”是指没有明显原因,但有什么可以为此类事件的原因是什么?

(注:我不是质疑循环练习。)

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

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

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,而不会永远等待一个随机事件?

回答

175

维基百科article on spurious wakeups有这么一个小节目:

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

摘要:如果一个Linux的进程发送信号的等待线程将分别享受一个不错的,热虚假唤醒

我买它。这是一个容易吞咽的药丸,而不是通常模糊的“这是为了表现”的原因。

8

Cameron Purdy写了一个blog post一会儿又回到了被虚假唤醒问题打中。所以,是的,它发生

我猜它在规范(如可能),因为其中一些Java的被部署在平台上的限制?虽然我可能是错的!

+5

http://www.jroller.com/cpurdy/entry/java_supplies_apologies_to_weird – akarnokd 2009-06-26 19:11:10

+0

我读了这篇文章,并给了我一个想法,即单元测试通过随机/确定性地唤醒单元测试来测试一个应用程序对循环等待范例的一致性。或者它已经在某个地方可用? – akarnokd 2009-06-26 19:17:28

+0

这是关于SO的另一个问题:“是否有*严格*虚拟机可用于测试?”。我很想看到一个严格的线程本地记忆 - 我不认为它们还存在 – 2009-06-26 20:01:53

20

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

7

我想补充这一点。是的,它发生了,我花了三天时间在24核心机器(JDK 6)上寻找多线程问题的原因。 10次​​处决中有4次没有任何模式。这从来没有发生在2核心或8核心上。

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

11

回答titile中的问题 - 是的!它happen.Though的Wiki article提到了一个很好的协议有关虚假唤醒了同样的,我碰到如下一个很好的解释 -

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

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

这使得有必要建立一个“合同”,可以无故通知等待线程。确切地说,会有一个原因 - 调度程序停电 - 但是由于线程被设计(出于一个很好的理由)忽略了调度程序的内部实现细节,这个原因可能更好地表现为“虚假”。

我从Source看这个答案,发现它足够合理。也读

Spurious wakeups in Java and how to avoid them