java多线程问题-虚假唤醒

虚假唤醒,个人觉得还是叫错误唤醒比较合适。
这是指线程在不应当被唤醒的情况下被唤醒后没有重新进入阻塞状态的问题。
例如,(经典问题)多线程实现对数字0的+1和-1操作的轮替,使得输出为1,0,1,0,1,0,…

/**
* num 初始化为 0
* 线程A和C实现对num的+1操作
* 线程B和D实现对num的-1操作
*/
public class Threads {
    public static void main(String[] args) {
        Number number = new Number();
        new Thread(() -> {for (int i = 0; i < 10; i++) number.increment();},"A").start();
        new Thread(() -> {for (int i = 0; i < 10; i++) number.increment();},"C").start();
        new Thread(() -> {for (int i = 0; i < 10; i++) number.decrement();},"B").start();
        new Thread(() -> {for (int i = 0; i < 10; i++) number.decrement();},"D").start();
    }
}
class Number {
    private int num = 0;
    public synchronized void increment() {
        if (num != 0) { // while (num != 0) {
            try {
                this.wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        num++;
        System.out.println(Thread.currentThread().getName() + "--->" + num);
        this.notifyAll();
    }
    public synchronized void decrement() {
        if (num == 0) { // while (num == 0) {
            try {
                this.wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        num--;
        System.out.println(Thread.currentThread().getName() + "--->" + num);
        this.notifyAll();
    }
}

这段代码实现了+1和-1操作,但是当你去运行它时,你很大概率将会看到错误的结果。这是因为出现了虚假唤醒问题。
考虑如下情况:

线程A 抢到锁 修改num为1
线程A 抢到锁 阻塞
线程C 抢到锁 阻塞
线程B 抢到锁 修改num为0 唤醒所有被阻塞线程
线程A 被唤醒 修改num为1 唤醒所有被阻塞线程
线程B 被唤醒 修改num为2

线程C 抢到锁 修改num为0
线程D 抢到锁 阻塞
线程C 抢到锁 阻塞
线程A 抢到锁 修改num为1 唤醒所有被阻塞线程
线程D 被唤醒 修改num为0 唤醒所有被阻塞线程
线程C 被唤醒 修改num为-1

流程如下:
虚假唤醒
在多线程并发的情况下,以上情况很大概率将会发生,这也是虚假唤醒发生的原因。

清楚了问题的所在,那么如何修改代码呢?
  问题在于if判断语句,if语句只会判断一次。当线程被唤醒后在不知道当前num值是否正确的情况下,冒着风险执行了后续操作。因此,将if替换为while,让线程再一次执行判断确保在得知num的正确性的情况下,再执行后续操作。

END

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值