一.生產者消費者問題? 1.生產者消費者問題(英語:Producer-consumer problem),也稱有限緩衝問題(英語:Bounded-buffer problem),是一個多線程同步問題的經典案例。該問題描述了兩個共用固定大小緩衝區的線程——即所謂的“生產者”和“消費者”——在實際運行時 ...
一.生產者消費者問題?
1.生產者消費者問題(英語:Producer-consumer problem),也稱有限緩衝問題(英語:Bounded-buffer problem),是一個多線程同步問題的經典案例。該問題描述了兩個共用固定大小緩衝區的線程——即所謂的“生產者”和“消費者”——在實際運行時會發生的問題。生產者的主要作用是生成一定量的數據放到緩衝區中,然後重覆此過程。與此同時,消費者也在緩衝區消耗這些數據。該問題的關鍵就是要保證生產者不會在緩衝區滿時加入數據,消費者也不會在緩衝區中空時消耗數據。
2.解決辦法:
要解決該問題,就必須讓生產者在緩衝區滿時休眠(要麼乾脆就放棄數據),等到下次消費者消耗緩衝區中的數據的時候,生產者才能被喚醒,開始往緩衝區添加數據。同樣,也可以讓消費者在緩衝區空時進入休眠,等到生產者往緩衝區添加數據之後,再喚醒消費者。通常採用進程間通信的方法解決該問題,常用的方法有信號燈法等。如果解決方法不夠完善,則容易出現死鎖的情況。出現死鎖時,兩個線程都會陷入休眠,等待對方喚醒自己。該問題也能被推廣到多個生產者和消費者的情形。
二.編碼實現生產者消費者問題
1.先上結果圖:我定義了三個生產者分別時張飛,趙雲還有關羽,兩個消費者分別時曹操還有劉備.
先上結果圖是為了說明:多線程說白了就是對cpu資源的搶奪,誰搶到了就可以執行自己的方法.
2.生產者消費者實現步驟:
(1).因為生產者跟消費者共用一塊區域,這裡我將這塊區域定義為“倉庫”。
(2).倉庫是有容量上限的,當數量達到上限後,生產者不允許繼續生產產品.當前線程進入等待狀態,等待其他線程喚醒。
(3).當倉庫沒有產品時,消費者不允許繼續消費,當前線程進入等待狀態,等待其他線程喚醒。
3.代碼的實現:
(1).生產者跟消費者之間消費的是產品,所有先定義一個產品類:
public class Product { //定義產品的唯一ID int id; //定義構造方法初始化產品id public Product(int id) { this.id=id; // TODO Auto-generated constructor stub } }
(2).定義一個倉庫用來存放產品.
public class Repertory { //定義一個集合類用於存放產品.規定倉庫的最大容量為10. public LinkedList<Product> store=new LinkedList<Product>(); public LinkedList<Product> getStore() { return store; } public void setStore(LinkedList<Product> store) { this.store = store; } /* 生產者方法 * push()方法用於存放產品. * 參數含義:第一個是產品對象 * 第二個是線程名稱,用來顯示是誰生產的產品. * 使用synchronized關鍵字修飾方法的目的: * 最多只能有一個線程同時訪問該方法. * 主要是為了防止多個線程訪問該方法的時候,將參數數據進行的覆蓋,從而發生出錯. */ public synchronized void push(Product p,String threadName) { /* 倉庫容量最大值為10,當容量等於10的時候進入等待狀態.等待其他線程喚醒 * 喚醒後繼續迴圈,等到倉庫的存量小於10時,跳出迴圈繼續向下執行準備生產產品. */ while(store.size()==10){ try { //列印日誌 System.out.println(threadName+"報告:倉庫已滿--->進入等待狀態--->呼叫老大過來消費"); //因為倉庫容量已滿,無法繼續生產,進入等待狀態,等待其他線程喚醒. this.wait(); } catch (InterruptedException e) { e.printStackTrace(); } } //喚醒所有等待線程 this.notifyAll(); //將產品添加到倉庫中. store.addLast(p); //列印生產日誌 System.out.println(threadName+"生產了:"+p.id+"號產品"+" "+"當前庫存來:"+store.size()); try { //為了方便觀察結果,每次生產完後等待0.1秒. Thread.sleep(100); } catch (InterruptedException e) { e.printStackTrace(); } } /* 消費者方法 * pop()方法用於存放產品. * 參數含義:線程名稱,用來顯示是誰生產的產品. * 使用synchronized關鍵字修飾方法的目的: * 最多只能有一個線程同時訪問該方法. * 主要是為了防止多個線程訪問該方法的時候,將參數數據進行的覆蓋,從而發生出錯. */ public synchronized void pop(String threadName){ /* 當倉庫沒有存貨時,消費者需要進行等待.等待其他線程來喚醒 * 喚醒後繼續迴圈,等到倉庫的存量大於0時,跳出迴圈繼續向下執行準備消費產品. */ while(store.size()==0) { try { //列印日誌 System.out.println(threadName+"下命令:倉庫已空--->進入等待狀態--->命令小弟趕快生產"); //因為倉庫容量已空,無法繼續消費,進入等待狀態,等待其他線程喚醒. this.wait(); } catch (InterruptedException e) { e.printStackTrace(); } } //喚醒所有等待線程 this.notifyAll(); //store.removeFirst()方法將產品從倉庫中移出. //列印日誌 System.out.println(threadName+"消費了:"+store.removeFirst().id+"號產品"+" "+"當前庫存來:"+store.size()); try { //為了方便觀察結果,每次生產完後等待1秒. Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } } }
(3).定義生產者
public class Producer implements Runnable { //定義一個靜態變數來記錄產品號數.確保每一個產品的唯一性. public static Integer count=0; //定義倉庫 Repertory repertory=null; //構造方法初始化repertory(倉庫) public Producer(Repertory repertory) { this.repertory=repertory; } /* run()方法因為該方法中存在非原子性操作count++; * 當多個線程同時訪問時會發生count++的多次操作,導致出錯 * 為該方法添加同步錯做,確保每一次只能有一個生產者進入該模塊。 * 這樣就能保證count++這個操作的安全性. */ @Override public void run() { while (true) { synchronized(Producer.class){ count++; Product product=new Product(count); repertory.push(product,Thread.currentThread().getName()); } } } }
(4).定義一個消費者
public class Consumer implements Runnable { //定義倉庫 Repertory repertory=null; //構造方法初始化repertory(倉庫) public Consumer(Repertory repertory) { this.repertory=repertory; } //實現run()方法,並將當前的線程名稱傳入. @Override public void run() { while(true){ repertory.pop(Thread.currentThread().getName()); } } }
(5).測試類
public class TestDemo { public static void main(String[] args) { //定義一個倉庫,消費者和生產者都使用這一個倉庫 Repertory repertory=new Repertory(); //定義三個生產者(p1,p2,p3) Producer p1=new Producer(repertory); Producer p2=new Producer(repertory); Producer p3=new Producer(repertory); //定義兩個消費者(c1,c2) Consumer c1=new Consumer(repertory); Consumer c2=new Consumer(repertory); //定義5個線程(t1,t2,t3,t4,t5) Thread t1=new Thread(p1,"張飛"); Thread t2=new Thread(p2,"趙雲"); Thread t3=new Thread(p3,"關羽"); Thread t4=new Thread(c1,"劉備"); Thread t5=new Thread(c2,"曹操"); //因為關羽跟趙雲的生產積極性高,所以把他們的線程優先順序調高一點 t2.setPriority(10); t3.setPriority(10); //啟動線程 t1.start(); t2.start(); t3.start(); t4.start(); t5.start(); } }
三.總結:
生產者消費者問題我也是弄了好幾天才略有頭緒,弄完了就把自己的認識貼了出來,歡迎各位指點