线程虚假唤醒

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

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
【社区内容提示】社区部分内容疑似由AI辅助生成,浏览时请结合常识与多方信息审慎甄别。
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容

友情链接更多精彩内容