生产者与消费者案例-虚假唤醒
以下是一個(gè)案例,有一個(gè)店員,負(fù)責(zé)進(jìn)貨和賣貨。進(jìn)貨生產(chǎn),賣貨消費(fèi)。
當(dāng)商品超過10件,生產(chǎn)等待,消費(fèi)繼續(xù),當(dāng)少于0件,消費(fèi)等待,消費(fèi)繼續(xù)。
正常代碼如下:
package com.atguigu.juc;/** 生產(chǎn)者和消費(fèi)者案例*/ public class TestProductorAndConsumer {public static void main(String[] args) {Clerk clerk = new Clerk();Productor pro = new Productor(clerk);Consumer cus = new Consumer(clerk);new Thread(pro, "生產(chǎn)者 A").start();new Thread(cus, "消費(fèi)者 B").start();} } //店員 class Clerk {private int product = 0;//進(jìn)貨public synchronized void get(){//循環(huán)次數(shù):0if(product >= 10){System.out.println("產(chǎn)品已滿!");try {this.wait();} catch (InterruptedException e) {}}System.out.println(Thread.currentThread().getName() + " : " + ++product);this.notifyAll();}//賣貨public synchronized void sale(){//product = 0; 循環(huán)次數(shù):0if(product <= 0){System.out.println("缺貨!");try {this.wait();} catch (InterruptedException e) {}}System.out.println(Thread.currentThread().getName() + " : " + --product);this.notifyAll();} }//生產(chǎn)者 class Productor implements Runnable{private Clerk clerk;public Productor(Clerk clerk) {this.clerk = clerk;}@Overridepublic void run() {for (int i = 0; i < 20; i++) {try {Thread.sleep(200);} catch (InterruptedException e) {}clerk.get();}} }//消費(fèi)者 class Consumer implements Runnable{private Clerk clerk;public Consumer(Clerk clerk) {this.clerk = clerk;}@Overridepublic void run() {for (int i = 0; i < 20; i++) {clerk.sale();}} }運(yùn)行結(jié)果:
很和諧沒問題!,生產(chǎn)者每次生產(chǎn)完就等待一下,導(dǎo)致消費(fèi)者搶到資源,這樣導(dǎo)致:0,1輪替。
但是,如果此時(shí)再假如一個(gè)生產(chǎn)者和消費(fèi)者:
public class TestProductorAndConsumer {public static void main(String[] args) {Clerk clerk = new Clerk();Productor pro = new Productor(clerk);Consumer cus = new Consumer(clerk);new Thread(pro, "生產(chǎn)者 A").start();new Thread(cus, "消費(fèi)者 B").start();new Thread(pro, "生產(chǎn)者 C").start();new Thread(cus, "消費(fèi)者 D").start();} }此時(shí)運(yùn)行結(jié)果:
?
可以看到,非常離譜!生產(chǎn)者數(shù)量為負(fù)數(shù),并且一直沒有停止的樣子。
分析:
假如最開始是缺貨狀態(tài),消費(fèi)者B和D進(jìn)入都是進(jìn)入等待的,此時(shí)一個(gè)生產(chǎn)者搶到資源,進(jìn)行生產(chǎn),完事生產(chǎn)了一件,
兩個(gè)消費(fèi)者同時(shí)喚醒,喚醒了之后,每個(gè)消費(fèi)者都繼續(xù)下面代碼,也就是wait下面的--product,導(dǎo)致數(shù)量為負(fù)數(shù)。
這個(gè)時(shí)候兩個(gè)消費(fèi)者再次進(jìn)入當(dāng)然還是等待,一個(gè)生產(chǎn)者再次進(jìn)入,當(dāng)然效果和上面一樣,再次數(shù)量在-1的基礎(chǔ)上,-1,-2。
?
這種現(xiàn)象叫做虛假喚醒。
?
為了解決這個(gè),其實(shí)JDK中已經(jīng)說明了,對于wait方法的使用,必須始終放在while循環(huán)中。
每次線程被喚醒之后都得重新進(jìn)入循環(huán),而不是直接執(zhí)行下面的--或者++操作。
修改如下:
把上面的if換成while即可:
package com.atguigu.juc;/** 生產(chǎn)者和消費(fèi)者案例*/ public class TestProductorAndConsumer {public static void main(String[] args) {Clerk clerk = new Clerk();Productor pro = new Productor(clerk);Consumer cus = new Consumer(clerk);new Thread(pro, "生產(chǎn)者 A").start();new Thread(cus, "消費(fèi)者 B").start();new Thread(pro, "生產(chǎn)者 C").start();new Thread(cus, "消費(fèi)者 D").start();} } //店員 class Clerk {private int product = 0;//進(jìn)貨public synchronized void get(){//循環(huán)次數(shù):0while(product >= 10){//為了避免虛假喚醒問題,應(yīng)該總是使用在循環(huán)中System.out.println("產(chǎn)品已滿!");try {this.wait();} catch (InterruptedException e) {}}System.out.println(Thread.currentThread().getName() + " : " + ++product);this.notifyAll();}//賣貨public synchronized void sale(){//product = 0; 循環(huán)次數(shù):0while(product <= 0){System.out.println("缺貨!");try {this.wait();} catch (InterruptedException e) {}}System.out.println(Thread.currentThread().getName() + " : " + --product);this.notifyAll();} }//生產(chǎn)者 class Productor implements Runnable{private Clerk clerk;public Productor(Clerk clerk) {this.clerk = clerk;}@Overridepublic void run() {for (int i = 0; i < 20; i++) {try {Thread.sleep(200);} catch (InterruptedException e) {}clerk.get();}} }//消費(fèi)者 class Consumer implements Runnable{private Clerk clerk;public Consumer(Clerk clerk) {this.clerk = clerk;}@Overridepublic void run() {for (int i = 0; i < 20; i++) {clerk.sale();}} }?
?
總結(jié)
以上是生活随笔為你收集整理的生产者与消费者案例-虚假唤醒的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: JavaScript二维数组
- 下一篇: React开发中使用fetch进行异步请