生产者与消费者案例-虚假唤醒
生活随笔
收集整理的這篇文章主要介紹了
生产者与消费者案例-虚假唤醒
小編覺得挺不錯的,現在分享給大家,幫大家做個參考.
以下是一個案例,有一個店員,負責進貨和賣貨。進貨生產,賣貨消費。
當商品超過10件,生產等待,消費繼續,當少于0件,消費等待,消費繼續。
正常代碼如下:
package com.atguigu.juc;/** 生產者和消費者案例*/ 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, "生產者 A").start();new Thread(cus, "消費者 B").start();} } //店員 class Clerk {private int product = 0;//進貨public synchronized void get(){//循環次數:0if(product >= 10){System.out.println("產品已滿!");try {this.wait();} catch (InterruptedException e) {}}System.out.println(Thread.currentThread().getName() + " : " + ++product);this.notifyAll();}//賣貨public synchronized void sale(){//product = 0; 循環次數:0if(product <= 0){System.out.println("缺貨!");try {this.wait();} catch (InterruptedException e) {}}System.out.println(Thread.currentThread().getName() + " : " + --product);this.notifyAll();} }//生產者 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();}} }//消費者 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();}} }運行結果:
很和諧沒問題!,生產者每次生產完就等待一下,導致消費者搶到資源,這樣導致:0,1輪替。
但是,如果此時再假如一個生產者和消費者:
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, "生產者 A").start();new Thread(cus, "消費者 B").start();new Thread(pro, "生產者 C").start();new Thread(cus, "消費者 D").start();} }此時運行結果:
?
可以看到,非常離譜!生產者數量為負數,并且一直沒有停止的樣子。
分析:
假如最開始是缺貨狀態,消費者B和D進入都是進入等待的,此時一個生產者搶到資源,進行生產,完事生產了一件,
兩個消費者同時喚醒,喚醒了之后,每個消費者都繼續下面代碼,也就是wait下面的--product,導致數量為負數。
這個時候兩個消費者再次進入當然還是等待,一個生產者再次進入,當然效果和上面一樣,再次數量在-1的基礎上,-1,-2。
?
這種現象叫做虛假喚醒。
?
為了解決這個,其實JDK中已經說明了,對于wait方法的使用,必須始終放在while循環中。
每次線程被喚醒之后都得重新進入循環,而不是直接執行下面的--或者++操作。
修改如下:
把上面的if換成while即可:
package com.atguigu.juc;/** 生產者和消費者案例*/ 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, "生產者 A").start();new Thread(cus, "消費者 B").start();new Thread(pro, "生產者 C").start();new Thread(cus, "消費者 D").start();} } //店員 class Clerk {private int product = 0;//進貨public synchronized void get(){//循環次數:0while(product >= 10){//為了避免虛假喚醒問題,應該總是使用在循環中System.out.println("產品已滿!");try {this.wait();} catch (InterruptedException e) {}}System.out.println(Thread.currentThread().getName() + " : " + ++product);this.notifyAll();}//賣貨public synchronized void sale(){//product = 0; 循環次數:0while(product <= 0){System.out.println("缺貨!");try {this.wait();} catch (InterruptedException e) {}}System.out.println(Thread.currentThread().getName() + " : " + --product);this.notifyAll();} }//生產者 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();}} }//消費者 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();}} }?
?
總結
以上是生活随笔為你收集整理的生产者与消费者案例-虚假唤醒的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: JavaScript二维数组
- 下一篇: 为你打开一扇门的影响