一般而言线程调用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方法一定是要获取到锁后,才会返回
参考 :