wait和notify的虚假唤醒(spurious wakeups)

文章目录


1 现象

这个词的定义来源于JDK的Object#wait()方法的注解
在这里插入图片描述
官方API明确的告诉我们,为了防止发生中断错误以及虚假唤醒的问题,我们需要将wait()方法放在while() 循环中使用

synchronized (obj) {
     while (<condition does not hold>) {
	     	obj.wait();
	}
      ... // Perform action appropriate to condition
}

实际上在我们去常使用Object的wait()(notify,notifyAll) 方法的时候,也必然伴随着如官方文档那样的标准的写法(while),那么,问题来了,什么是虚假唤醒(spurious wakeups)

2 虚假唤醒

虚假唤醒(spurious wakeups)的现象一般是伴随着当另一个持有了锁的线程去notify(或者是notifyAll())的时候,锁等待(状态:Thread.State = WAITING)在wait()方法上的线程会被唤醒,并重新竞争锁,竞争到锁的线程会继续执行wait()方法之后的代码【注意这里不是重新去执行一遍锁方法】

这里我们举一个例子:(该代码示例来源于网站)


static class Buf {
    private final int MAX = 5;
    private final ArrayList<Integer> list = new ArrayList<>();
    synchronized void put(int v) throws InterruptedException {
        if (list.size() == MAX) {
            wait();
        }
        list.add(v);
        notifyAll();
    }
 
    synchronized int get() throws InterruptedException {
        // line 0 
        if (list.size() == 0) {  // line 1
            wait();  // line2
            // line 3
        }
        int v = list.remove(0);  // line 4
        notifyAll(); // line 5
        return v;
    }
 
    synchronized int size() {
        return list.size();
    }
}

假设现在有 A,B 两个线程来执行 get 操作,我们假设如下的步骤发生了:

  1. A 拿到了锁 line 0。

  2. A 发现 size==0, (line 1),然后进入等待,并释放锁 (line 2)。

  3. 此时 B 拿到了锁,line0,发现 size==0,(line 1),然后进入等待,并释放锁 (line 2)。

  4. 这个时候有个线程 C 往里面加了个数据 1,那么 notifyAll 所有的等待的线程都被唤醒了。

  5. AB 重新获取锁,假设又是 A 拿到了。然后他就走到 line 3,移除了一个数据,(line4) 没有问题。

  6. A 移除数据后想通知别人,此时 list 的大小有了变化,于是调用了 notifyAll (line5),这个时候就把 B 给唤醒了,那么 B 接着往下走。

  7. 这时候 B 就出问题了,因为其实此时的竞态条件已经不满足了 (size==0)。B 以为还可以删除就尝试去删除,结果就跑了异常了。

上面的这个现象就是虚假唤醒,在if条件下对于条件的判断,都是会触发虚假唤醒,因为持有锁去notify 唤醒在等待wait()的时候,if条件只会去判断一次,如果不通过while判断的化,实际上对于不满足条件下的唤醒操作都是无效操作。

对于这种问题的解决,按照官方文档的指示:都是通过将if条件修改成while

这也解释了为什么wait() 和notify() 方法一般都是在while(条件变量)代码体内

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值