synchronized的虚假唤醒

文章讨论了Java中`push`方法使用`while(!isEmpty)`而非`if(!isEmpty)`的原因,强调了在多线程环境下避免虚假唤醒的重要性。作者通过实例解释了虚假唤醒的概念,以及为何在条件检查后等待是必要的。
摘要由CSDN通过智能技术生成

原文出处

  • 这段代码中为什么使用while(!isEmpty)而不是if(!isEmpty),这里引出虚假唤醒问题
  public synchronized void push(String name,int age){
        try {
            //不能用 if,因为可能有多个线程,导致虚假唤醒
            while(!isEmpty){//进入到while语句内,说明 isEmpty==false,那么表示有数据了,不能生产,必须要等待消费者消费
                this.wait();//导致当前线程等待,进入等待池中,只能被其他线程唤醒
            } 为什么使用while(!isEmpty)而不是if(!isEmpty)
             
            //-------生产数据开始-------
            this.name = name;
            //延时代码
            Thread.sleep(10);
            this.age = age;
            //-------生产数据结束-------
            isEmpty = false;//设置 isEmpty 为 false,表示已经有数据了
            this.notifyAll();//生产完毕,唤醒所有消费者
        } catch (Exception e) {
            e.printStackTrace();
        }
         
    }
  • 官方解释什么是虚假唤醒:

    线程也可以唤醒,而不会被通知,中断或超时,即所谓的虚假唤醒 。 虽然这在实践中很少会发生,但应用程序必须通过测试应该使线程被唤醒的条件来防范,并且如果条件不满足则继续等待。 换句话说,等待应该总是出现在循环中,就像这样:
    synchronized (obj) {
    while ()
    obj.wait(timeout);
    … // Perform action appropriate to condition
    }

  • 举例说明什么是虚假唤醒:
    (1)首先初始num为0,A线程将一个元素入队,此时num=1;
    (2)B线程从队列中获取了一个元素,此时num = 0。
    (3)D线程也想从队列中获取一个元素,但此时num = 0,D线程便只能进入阻塞(decrease.wait()),等待 num > 0。
    (4)这时,C线程将一个元素入队,并调用incresement.notify()唤醒条件变量。
    (5) 处于等待状态的D线程接收到C线程的唤醒信号,便准备解除阻塞状态,执行接下来的任务(获取队列中的元素)。
    (6) 然而可能出现这样的情况:当D号线程准备获得队列的锁,去获取队列中的元素时,此时B号线程刚好执行完之前的元素操作,返回再去请求队列中的元素,B线程便获得队列的锁,检查到num > 0,就获取到了C号线程刚刚入队的元素,然后释放队列锁。
    (7) 等到D线程获得队列锁,判断发现num == 0,B线程“偷走了”这个元素,所以对于D线程而言,这次唤醒就是“虚假”的,它需要再次等待num > 0。

  • 4
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值