一篇给你分析懂生产者与消费者问题为什么使用while不使用if(JUC)

java生产者与消费者为什么使用while不使用if?
**

先说结论:
if 只判断1次,然后就进入等待,唤醒后直接执行后面的操作,如果等待的线程很多,就会全部执行,乱只是时间问题
while 是循环判断,等待被唤醒也会再次判断number!=0是否成立,一个线程运行了,其他线程判断为true,也只能再次进入wait等待

我们先看一下使用if时会出现错误的例子,想深入了解就看看

**

JAVA代码示例

package com.juc.producerandconsumer;

public class Syn1 {
    public static void main(String[] args) {
        Data data = new Data();
        new Thread(() -> {
            for (int i = 0; i < 5; i++) {
                data.producer();
            }
        }, "B线程").start();
        new Thread(() -> {
            for (int i = 0; i < 5; i++) {
                data.consumer();
            }
        }, "A线程").start();
        new Thread(() -> {
            for (int i = 0; i < 5; i++) {
                data.consumer();
            }
        }, "C线程").start();
        new Thread(() -> {
            for (int i = 0; i < 5; i++) {
                data.producer();
            }
        }, "D线程").start();
    }
}

class Data{
    private int number=0;
    //消费者-1
    public synchronized void consumer(){
        if(number==0){
            try {
                System.out.println(Thread.currentThread().getName()+"判断后等待");
                this.wait();//等待
                System.out.println(Thread.currentThread().getName()+"开始执行");

            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        number--;
        System.out.println(Thread.currentThread().getName()+"-1执行: "+number);
        this.notifyAll();
    }

    //生产者+1
    public synchronized void producer(){
        if(number!=0){
            try {
                System.out.println(Thread.currentThread().getName()+"判断后进行等待");
                this.wait();
                System.out.println(Thread.currentThread().getName()+"开始执行");
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        number++;
        System.out.println(Thread.currentThread().getName()+"+1执行: "+number);
        this.notifyAll();
    }

}

上面的代码是简单的多生产者与多消费者,简单的解释就是:number为0时+1,number不为0时-1,运行结果应该是1与0循环
注:生产消费方法都加了synchronized,同时间只能一个线程运行

运行结果:
B线程+1执行: 1
B线程判断后进行等待
D线程判断后进行等待
C线程-1执行: 0
C线程判断后等待
A线程判断后等待
D线程开始执行
D线程+1执行: 1
D线程判断后进行等待
B线程开始执行
B线程+1执行: 2
B线程判断后进行等待
D线程开始执行
D线程+1执行: 3
D线程判断后进行等待
B线程开始执行
B线程+1执行: 4
B线程判断后进行等待
D线程开始执行
D线程+1执行: 5
D线程判断后进行等待
B线程开始执行
B线程+1执行: 6
B线程判断后进行等待
D线程开始执行
D线程+1执行: 7
D线程判断后进行等待
B线程开始执行
B线程+1执行: 8
D线程开始执行
D线程+1执行: 9
A线程开始执行
A线程-1执行: 8
A线程-1执行: 7
A线程-1执行: 6
A线程-1执行: 5
A线程-1执行: 4
C线程开始执行
C线程-1执行: 3
C线程-1执行: 2
C线程-1执行: 1
C线程-1执行: 0

进程已结束,退出代码0

这里B和D是 -1 A和C是 +1
可以看到,最开始是正常的,然后哪里出现问题,一直是B和D循环,最后一直A,然后一直C

这个问题困扰了好久,通过各方面了解,分析一下我的理解

官方 文档的解释是

在这里插入图片描述

什么是虚假唤醒?

虚假唤醒就是在多线程执行过程中,线程间的通信未按照我们幻想的顺序唤醒,故出现数据不一致等不符合我们预期的结果。
关键是:if()导致了这个虚假唤醒的问题

先把if改成while运行一下
B线程+1执行: 1
B线程判断后进行等待
D线程判断后进行等待
C线程-1执行: 0
C线程判断后等待
A线程判断后等待
D线程开始执行
D线程+1执行: 1
D线程判断后进行等待
B线程开始执行
B线程判断后进行等待
A线程开始执行
A线程-1执行: 0
A线程判断后等待
C线程开始执行
C线程判断后等待
B线程开始执行
B线程+1执行: 1
B线程判断后进行等待
D线程开始执行
D线程判断后进行等待
C线程开始执行
C线程-1执行: 0
C线程判断后等待
A线程开始执行
A线程判断后等待
D线程开始执行
D线程+1执行: 1
D线程判断后进行等待
B线程开始执行
B线程判断后进行等待
A线程开始执行
A线程-1执行: 0
A线程判断后等待
C线程开始执行
C线程判断后等待
B线程开始执行
B线程+1执行: 1
B线程判断后进行等待
D线程开始执行
D线程判断后进行等待
C线程开始执行
C线程-1执行: 0
C线程判断后等待
A线程开始执行
A线程判断后等待
D线程开始执行
D线程+1执行: 1
D线程判断后进行等待
B线程开始执行
B线程判断后进行等待
A线程开始执行
A线程-1执行: 0
A线程判断后等待
C线程开始执行
C线程判断后等待
B线程开始执行
B线程+1执行: 1
B线程判断后进行等待
D线程开始执行
D线程判断后进行等待
C线程开始执行
C线程-1执行: 0
C线程判断后等待
A线程开始执行
A线程判断后等待
D线程开始执行
D线程+1执行: 1
D线程判断后进行等待
B线程开始执行
B线程判断后进行等待
A线程开始执行
A线程-1执行: 0
A线程判断后等待
C线程开始执行
C线程判断后等待
B线程开始执行
B线程+1执行: 1
D线程开始执行
D线程判断后进行等待
C线程开始执行
C线程-1执行: 0
A线程开始执行
A线程判断后等待
D线程开始执行
D线程+1执行: 1
A线程开始执行
A线程-1执行: 0

进程已结束,退出代码0

可以看出程序正常执行,有兴趣的可以自己动手试一下

为什么会出现这种错误呢?

A、C是消费者number!=0时-1,B、D为生产者number==0时+1
仔细分析一下 if 判断:
1、number=0
当使用 if 判断时,当B、D判断number!=0不成立时,就会执行 number++
2、number=1
number!=0成立,B 就开始等待wait,这时若运行D线程,D也会进入等待。
B、D会等待A或C运行number–,运行结果来看,C线程先抢到时间片,if判断失败后,number–,A线程运行时if判断为true,进入等待
问题就在这里
C线程执行notifyAll()后,会唤醒B、D也就是等待中的所有线程
notifyAll()唤醒正在等待此对象监视器锁的所有线程。
3、number=0
看执行情况,这时D线程抢先拿到时间片,先执行number++,然后B线程拿到时间片,执行number++,这时number=2
4、number=2
这是number!=0一直成立,number++反复执行,D唤醒B,B唤醒D这样反复循环。

在这期间A、C也被唤醒了,只是这里的线程只运行5次,很快就运行结束了,如果改的大一点,应该更乱。

看到这里,相信很多人已经看出来问题所在

if 只判断1次,然后就进入等待,唤醒后直接执行后面的操作,如果等待的线程很多,就会全部执行,乱只是时间问题
while 是循环判断,等待被唤醒也会再次判断number!=0是否成立,一个线程运行了,其他线程判断为true,也只能再次进入wait等待

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值