线程虚假唤醒

2017-06-20  本文已影响0人  huashu

一般而言线程调用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)的讨论

上一篇 下一篇

猜你喜欢

热点阅读