1. 程式人生 > >JAVA 多執行緒經典案例-生產者消費者模型【使用wait/notify實現】

JAVA 多執行緒經典案例-生產者消費者模型【使用wait/notify實現】

 

生產者消費者模型實現細節

生產者生產產品到公共倉庫,消費者消費公共倉庫中產品。

情況一:當公共倉庫產品達到倉庫容量上限,生產者停止生產;

情況二:當公共倉庫沒有產品,或達到設定的倉庫容量下限,消費者停止消費;

情況三:當公共倉庫產品達到容量上限時,消費者消費一次,就可以讓生產者繼續生產;

情況四:當公共倉庫沒有產品時,生產者生產一次,就可以讓消費者繼續消費;

功能實現說明

公共倉庫 ,包含“倉庫容量上限”、“倉庫容量下限”、“當前容量”基礎屬性,及“入庫”、“出庫”基礎方法;

生產者 ,包含“所屬倉庫”、“每次生產數量”基礎屬性,及“生產存入倉庫”基礎方法;

消費者 ,包含“所屬倉庫”、“每次消費數量”基礎屬性,及“倉庫取出消費”基礎方法;

程式碼實現

公共倉庫 ,抽取“入庫”、“出庫”基礎方法作為介面,後續可通過實現該介面,重寫方法來實現;

(1)公共倉庫介面

 
/** * @date 2018年12月11日 * @method putIn(int num) //往倉庫中存入指定數量的產品 * @method outOf(int num) //從倉庫中取出制定數量的產品 */ interface AbstractStorage { void putIn(int num); void outOf(int num); } 

(2)倉庫實現類

倉庫類只需判斷庫存量是否達到存入/取出閾值,從而限制生產者/消費者的存入/取出,不需要使用迴圈來判斷;

 
/** * @date 2018年12月11日 * @param MAX_PRODUCT //倉庫容量上限 * @param MIN_PRODUCT //倉庫容量下限 * @param stock //倉庫當前容量,屬於共享資源 * @method putIn(int) //當(庫存+即將存入)大於容量上限,停止存入 * @method outOf(int) //當(庫存-即將取出)小於容量上限,停止取出 */ class Storage implements AbstractStorage { public final int MAX_PRODUCT = 10; public final int MIN_PRODUCT = 0; private int stock; @Override public synchronized void putIn(int num) { if ((stock + num) >= MAX_PRODUCT) { System.out.println("庫存已滿!"); try { wait(); } catch (InterruptedException e) { e.printStackTrace(); } } else { stock += num; System.out.println(Thread.currentThread().getName() + "存入,庫存:" + stock); notifyAll(); } } @Override public synchronized void outOf(int num) { if ((stock - num) <= MIN_PRODUCT) { System.out.println("庫存已空!"); try { wait(); } catch (InterruptedException e) { e.printStackTrace(); } } else { stock -= num; System.out.println(Thread.currentThread().getName() + "取出,庫存:" + stock); notifyAll(); } } public int getStock() { return stock; } public void setStock(int stock) { this.stock = stock; } } 

生產者

使用實現Runnable介面方式建立生產者執行緒,將生產者生產操作放到重寫的run()方法中,實現生產者執行緒生產;

 
/** * @Data 2018年12月11日 * @param storage //公共倉庫 * @param num //生產數量 * @constructor Produce(AbstractStorage) //構造器 * @method prod(int) //生產者生產方法 * @method run() //生產者執行緒執行方法 * @method setNum(int) //設定生產者每次生產數量 */ class Producer implements Runnable { private AbstractStorage storage; private int num; public Producer(AbstractStorage storage) { this.storage = storage; } @Override public void run() { prod(num); } private void prod(int num) { while (true) { try { Thread.sleep(100); //需要新增延時,不然生產者執行緒獲得執行權時,一瞬間即可生產到倉庫上限值 } catch (InterruptedException e) { e.printStackTrace(); } storage.putIn(num); } } public void setNum(int num) { this.num = num; } } 

消費者

使用實現Runnable介面方式建立消費者執行緒,將消費者消費操作放到重寫的run()方法中,實現消費者執行緒消費;

 
/** * @Data 2018年12月11日 * @param storage //公共倉庫 * @param num //生產數量 * @constructor Consume(AbstractStorage) //構造器 * @method cons(int) //消費者生產方法 * @method run() //消費者執行緒執行方法 * @method setNum(int) //設定消費者每次生產數量 */ class Consumer implements Runnable { private AbstractStorage storage; private int num; public Consumer(AbstractStorage storage) { this.storage = storage; } @Override public void run() { cons(num); } private void cons(int num) { while (true) { try { Thread.sleep(100); //需要新增延時,不然消費者執行緒獲得執行權時,一瞬間即可消費到倉庫下限值 } catch (InterruptedException e) { e.printStackTrace(); } storage.outOf(num); } } public void setNum(int num) { this.num = num; } 

測試類

 
/** * @Date 2018年12月11日 * @param storage //建立公共倉庫 * @param producer //建立生產者執行緒 * @param consumer //建立消費者執行緒 */ public class TestA { public static void main(String[] args) { AbstractStorage storage = new Storage(); Producer producer = new Producer(storage); Consumer consumer = new Consumer(storage); producer.setNum(2); consumer.setNum(1); Thread t1 = new Thread(producer); Thread t2 = new Thread(consumer); t1.setName("producer"); t2.setName("consumer"); t1.start(); t2.start(); } } 

測試輸出

寫在最後的話

使用wait/notify方法實現,很多時候會出現一些奇怪的問題,例如:

1、兩者都持有鎖且等待對方釋放鎖,造成死鎖的情況;

2、對未持有鎖的物件進行釋放鎖,導致IllegalMonitorStateException異常;

3、加鎖時,雙方所加的鎖並非同一把鎖,導致資料異常;

關於生產者/消費者模型,現在更多是使用阻塞佇列(Queue)的方式來實現,後續會接著去學習那一塊知識。

【注】以上為本人通過網上視訊教程以及例項程式碼整理所得,如果出錯以及更好的提議,歡迎留言指正,一起學習。

---------------------

每天都在分享文章,也每天都有人想要我出來給大家分享下怎麼去學習Java。大家都知道,我們是學Java全棧的,大家就肯定以為我有全套的Java系統教程。沒錯,我是有Java全套系統教程,進扣裙【47】974【9726】所示,進群的時候記得表明自己想要學習什麼,不要用小號,這樣小編才好給你們發定向資源,今天小編就免費送!~

 

 

後記:對於大部分轉行的人來說,找機會把自己的基礎知識補齊,邊工作邊補基礎知識,真心很重要。“我們相信人人都可以成為一個程式設計師,現在開始,找個師兄,帶你入門,學習的路上不再迷茫。這裡是ja+va修真院,初學者轉行到網際網路行業的聚集地。"