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等待