关于使用if和while包裹wait方法

结论:在需要先判断条件,再使用wait方法的情况下,使用while。原因是:线程使用wait放弃锁后,线程会停在wait方法的位置,等到线程被notify唤醒,并重新获得锁,线程会继续执行wait方法后面的操作。如果使用if语句块,线程重新获取锁并执行完if块中的语句后,不会再判断一次if条件,而是直接执行if外的语句,从而引起错误的执行流程。
错误示例代码:

/**
 * Author: Aipande
 * Description:生产者消费者模型
 * Date-Of-Create:2020/10/22-8:45
 */
@SuppressWarnings("all")
public class PCDemo {

    public static void main(String[] args) throws InterruptedException {
    	//共享资源
        Product product = new Product(0);
        //消费者
        new Thread(()->{
            synchronized (product){
                while (true){
                    //判断
                    if (product.number<=0){
                        try {
                            //期望唤醒生产者线程
                            product.notifyAll();
                            product.wait();
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }
                    //执行
                    product.sale();
                }
            }
        },"B消费者").start();

        //消费者
        new Thread(()->{
            synchronized (product){
                while (true){
                    //判断
                    if (product.number<=0){
                        try {
                            //期望唤醒生产者线程
                            product.notifyAll();
                            product.wait();
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }
                    //执行
                    product.sale();
                }
            }
        },"C消费者").start();
        TimeUnit.SECONDS.sleep(1);
        //生产者
        new Thread(()->{
            synchronized (product){
                while (true){
                    //判断
                    if(product.number>0){
                        //等待
                        try {
                            //唤醒消费者线程
                            product.notifyAll();
                            product.wait();//锁在此处释放以后,线程就停留在这里,等待获取锁后继续执行
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }
                    //执行
                    product.add();
                }
            }
        },"A生产者").start();

    }
}

class Product{
    int number;

    public Product(int number) {
        this.number = number;
    }

    public void add(){
        System.out.println(Thread.currentThread().getName()+"生产了一件商品,剩余"+(++number));
    }

    public void sale(){
        System.out.println(Thread.currentThread().getName()+"消费了一件商品,剩余"+(--number));
    }
}

如下结果:

B消费者消费了一件商品,剩余-1
C消费者消费了一件商品,剩余-2
B消费者消费了一件商品,剩余-3
C消费者消费了一件商品,剩余-4
B消费者消费了一件商品,剩余-5
C消费者消费了一件商品,剩余-6
B消费者消费了一件商品,剩余-7
C消费者消费了一件商品,剩余-8
......

我们分析一下流程:
1.主线程休眠1秒,消费者B先获取锁,此时商品数量为0,B线程Notify没啥作用,B线程wait。
2.消费者C获取锁,此时商品数量为0,C线程notify唤醒B线程,然后wait。
3.消费者B线程被C线程唤醒,执行if块外的sale代码,此时商品数量为-1,然后又唤醒C线程,自己wait。
4.C线程被B线程唤醒,执行if块外sale代码,商品数量变为-2,然后再唤醒B线程,自己wait。

循环往复,就出现上述结果。
究其原因,还是线程被唤醒后,没有重新再校验if条件,所以使用while条件判断包裹wait更加合适。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值