美文网首页
线程虚假唤醒

线程虚假唤醒

作者: huashu | 来源:发表于2017-06-20 14:25 被阅读0次

    一般而言线程调用wait()方法后,需要其他线程调用notify,notifyAll方法后,线程才会从wait方法中返回, 而虚假唤醒(spurious wakeup)是指线程通过其他方式,从wait方法中返回。

    下面是一个买票退票的操作例子,买票时,线程A买票,如果发现没有余票,则会调用wait方法,线程进入等待队列中,线程B进行退票操作,余票数量加一,然后调用notify 方法通知等待线程,此时线程A被唤醒执行购票操作。

    从程序的顺序性来看 if (remainTicketNum<=0)没有问题,但是为什么会出现虚假唤醒呢?

    因为wait方法可以分为三个操作:

    (1)释放锁并阻塞

    (2)等待条件cond发生

    (3)获取通知后,竞争获取锁

    假设此时有线程A,C买票,线程A调用wait方法进入等待队列,线程C买票时发现线程B在退票,获取锁失败,线程C阻塞,进入阻塞队列,线程B退票时,余票数量+1(满足条件2 等待条件发生),线程B调用notify方法后,线程C马上竞争获取到锁,购票成功后余票为0,而线程A此时正处于wait方法醒来过程中的第三步(竞争获取锁获取锁),当线程C释放锁,线程A获取锁后,会执行购买的操作,而此时是没有余票的。

    解决的办法是条件判断通过while(remainTicketNum<=0)来解决,但是有个问题是如果一直没有退票操作线程Notify,while语句会一直循环执行下去,CPU消耗巨大

    public class SpuriousWakeUp {

    static Object lock=newObject();

    static  int remainTicketNum=0;

    public void buyTicket() {

    synchronized(lock) {

    while(remainTicketNum<=0) {//if (remainTicketNum<=0)虚假唤醒

    try{

    lock.wait();

    }catch(InterruptedException e) {

    e.printStackTrace();

    }}

    remainTicketNum--;

    System.out.println(Thread.currentThread().getName() +"购买成功");

    }}

    public voidreturnTicket() {

    synchronized(lock) {

    remainTicketNum++;

    lock.notify();

    System.out.println(Thread.currentThread().getName() +"退票成功");

    }}}

    PS:wait方法一定是要获取到锁后,才会返回

    参考 :

     Java多线程:一道阿里面试题的分析与应对

    对条件变量(condition variable)的讨论

    相关文章

      网友评论

          本文标题:线程虚假唤醒

          本文链接:https://www.haomeiwen.com/subject/owjoqxtx.html