线程的虚假唤醒(Spurious Wakeups)以及解决方案

网上一堆误人子弟的文章,有说是别的线程唤醒了这个线程,然后唤醒后唤醒条件被某个线程改了导致不成立这种说法,还有说的是唤醒来后没有执行条件,无效唤醒等等等,都在乱讲。

先说结论:
虚假唤醒(Spurious Wakeups):
一个线程可以从挂起状态变为可以运行状态(也就是被唤醒)即使该线程没有被其他线程调用notify(),notify()方法进行通知,或者被中断,或者等待超时。
注意:
解决方法就是不停的去测试该线程被唤醒的条件是否满足,不满足则继续等待。也就是在一个循环中调用wait()方法进行防范,退出循环的条件是满足该唤醒线程的条件。

参见官网:
http://tutorials.jenkov.com/java-concurrency/thread-signaling.html 需要

线程的虚假唤醒(Spurious Wakeups)以及解决方案_第1张图片

由于莫名其妙的原因,即使尚未调用notify()和notifyAll(),也可以唤醒线程。 这被称为虚假唤醒。 没有任何理由醒来。

如果在MyWaitNofity2类的doWait()方法中发生虚假唤醒,则等待线程可以继续处理而不会收到正确的信号! 这可能会导致应用程序出现严重问题。

为防止虚假唤醒,在while循环内而不是if语句内部检查信号成员变量。 这样的while循环也称为自旋锁。 唤醒的线程旋转,直到自旋锁(while循环)中的条件变为false。 以下是MyWaitNotify2的修改版本,显示了以下内容:

public class MyWaitNotify3{

  MonitorObject myMonitorObject = new MonitorObject();
  boolean wasSignalled = false;

  public void doWait(){
    synchronized(myMonitorObject){
      while(!wasSignalled){
        try{
          myMonitorObject.wait();
         } catch(InterruptedException e){...}
      }
      //clear signal and continue running.
      wasSignalled = false;
    }
  }

  public void doNotify(){
    synchronized(myMonitorObject){
      wasSignalled = true;
      myMonitorObject.notify();
    }
  }
}

注意wait()调用现在嵌套在while循环而不是if语句中。 如果等待的线程在没有收到信号的情况下唤醒,则wasSignalled成员仍将为false,并且while循环将再次执行,导致唤醒的线程返回等待。

知乎上的答案:
虚假唤醒(spurious wakeup)是一个表象,即在多处理器的系统下发出wait的程序有可能在没有notify唤醒的情形下苏醒继续执行。以运行在linux的hotspot虚拟机上的java程序为例,wait方法在jvm执行时实质是调用了底层pthread_cond_wait/pthread_cond_timedwait函数,挂起等待条件变量来达到线程间同步通信的效果,而底层wait函数在设计之初为了不减慢条件变量操作的效率并没有去保证每次唤醒都是由notify触发,而是把这个任务交由上层应用去实现,即使用者需要定义一个循环去判断是否条件真能满足程序继续运行的需求,当然这样的实现也可以避免因为设计缺陷导致程序异常唤醒的问题。

你可能感兴趣的:(多线程并发,并发编程)