結合生活,剖析《生產者消費者模型》-java多執行緒(一)
部落格園的園友們好,看部落格園上各位大佬的文章,已陪伴了我程式設計師職業的三年,
如今自己同樣希望能把自己從小白到菜鳥的成長過程分享給大家。不定期更新!!!
首先我本人智商不高,理解問題十分吃力,完全不屬於天才的行列,因此學習每一個知識
都喜歡刨根問底,結合生活,徹徹底底理解知識的本質!
進入正題,這篇文章,主要站在一個初學者的角度,結合經典的“生產者消費者模型”,寫一個java多執行緒例子!
首先解釋幾個概念:
1、#程序:通俗的講,就是一個程式一次執行的過程。是系統進行資源分配和排程的一個獨立單位。
2、#執行緒:一個程序的生命週期,由一個或若干個執行緒完成。是CPU排程和分派的基本單位。
3、#並行:同一時間點或者時間段,可以處理超過一個任務的能力。
eg:你正在lol,女朋友來電話,於是你單手操作或者側頭夾住手機,既聊天又打團,這就是你自己並行處理了,
撩妹和打遊戲。這個過程,你就類似於cpu,前提你是多核。
4、#併發:主要針對多執行緒提出的概念。可以在一個時間段,交替執行不同事情的能力。
eg:你正在lol,女朋友來電話,於是你掛機去接電話,然後打完電話回來面對騰訊的裁決。或者你等著打完團,然後
面對女友的生氣。這就是併發,你交替執行了不同事情。這個過程,你也類似於cpu,可以不用多核。
二、簡單程式碼實現多執行緒
2.1模型圖
註釋:張全蛋經過自己的努力,進入富士康工廠工作,主要負責生產時下流行的iPhoneXs,然後廣大果粉在庫存充足的情況下購買iPhoneXs。
這個過程共涉及以下角色和過程:
1、生產者(張全蛋)。
2、消費者(廣大果粉)。
3、產品(iPhoneXs)。
4、生產產品,購買產品。
2.2 程式展示
2.2.1 產品類
由於我們此次實驗過程,主要涉及生產產品,所以我們可以忽略產品本身具有的屬性和方法。
1 package com.dcits.weipt; 2 3 /** 4 * 產品實體類 5 * 因我們此次實驗是針對生產產品 6 * 所以我們忽略產品本身所具有的方法和屬性 7 * @author weipt 8 * @date 20180915*/ 9 public class Product {10 11 }
2.2.2 工廠類
工廠類用於生產產品,需要注意的是,在java中,生產一個產品,就是new一個產品類的實體物件。
1 package com.dcits.weipt; 2 3 import java.util.ArrayList; 4 import java.util.List; 5 6 /** 7 * 生產產品的工廠類 8 * 生產一個產品則意味著new一個產品物件 9 * 消費一個產品則意味著remove一個物件 10 * @author weipt 11 * @date 20180915*/ 12 13 14 public class ProductFactory { 15 private List<Product> list = new ArrayList<Product>(); //利用list儲存物件 16 17 /** 18 * 工廠生產產品的方法 19 * 單位時間生產3個 20 * */ 21 public void makeProduct() { 22 for(int i=0;i<3;i++) { 23 list.add(new Product()); 24 } 25 } 26 27 /** 28 * 工廠消費產品的方法 29 * 單位時間消費1個*/ 30 public void moveProduct() { 31 list.remove(0); 32 } 33 34 /** 35 * 獲取產品個數*/ 36 public int getNum() { 37 return list.size(); 38 } 39 40 }
2.2.3 生產者
用於不斷呼叫工廠類,生產產品。每當我們生產一次產品,需要呼叫notify/notifyAll通知或喚醒消費者來購買。
但當我們生產超過max,需要呼叫wait,等待消費者購買,減少庫存。
1 package com.dcits.weipt; 2 3 /** 4 *生產者實體類 5 * @author weipt 6 * @date 20180915*/ 7 8 public class Producer implements Runnable { 9 private final int MAX_PRODUCT = 30; 10 private ProductFactory pf; 11 12 public Producer(ProductFactory pfIn) { 13 this.pf = pfIn; 14 } 15 16 @Override 17 public void run() { 18 produce(); 19 } 20 21 /** 22 * 生產者生產,產品 23 */ 24 public void produce() { 25 while (true) { 26 synchronized (pf) { 27 if (pf.getNum() >= MAX_PRODUCT) { 28 try { 29 System.out.println("warnning! 庫存已滿,請稍微再生產!"); 30 pf.wait(); 31 } catch (InterruptedException e) { 32 e.printStackTrace(); 33 } 34 }else { 35 pf.makeProduct(); 36 System.out.println("P---》生產者生產了第【" + pf.getNum() + "】個產品"); 37 pf.notifyAll(); 38 } 39 try { 40 Thread.sleep(1000); 41 } catch (InterruptedException e) { 42 // TODO Auto-generated catch block 43 e.printStackTrace(); 44 } 45 } 46 } 47 } 48 }
2.2.4 消費者
用於不斷呼叫工廠類,購買產品。每當我們購買一次產品,需要呼叫notify/notifyAll通知或喚醒生產者去生產。
但當庫存小於min,需要呼叫wait,等待生產者,增加庫存。
1 package com.dcits.weipt; 2 3 /** 4 * 消費者實體類 5 * @author weipt 6 * @date 20180915*/ 7 8 public class Consumer implements Runnable { 9 private final int MIN_PRODUCT = 0; //產品最小值 10 private ProductFactory pf; //產品工廠物件 11 12 /** 13 * 通過構造方法獲取產品物件*/ 14 public Consumer(ProductFactory pfIn) { 15 this.pf = pfIn; 16 } 17 18 @Override 19 public void run() { 20 consume(); 21 } 22 23 /** 24 * 消費者從庫存中取產品 25 */ 26 public void consume() { 27 while (true) { 28 synchronized (pf) { //因為生產者和消費者都是對產品操作,所以對產品進行加鎖 29 if (pf.getNum() <= MIN_PRODUCT) { 30 try { 31 System.out.println("warnning! 庫存已空,請稍微再取!");//注意順序,要放到wait之前 32 pf.wait(); //等待其他執行緒操作,直到收到其他執行緒的notify 33 //此處我沒有寫notify,因為庫存缺貨,就只能等生產者生產,喚醒其他消費者沒有用 34 } catch (InterruptedException e) { 35 e.printStackTrace(); 36 } 37 }else { 38 System.out.println("C---》消費者取走了第【" + pf.getNum() + "】個產品"); 39 pf.moveProduct(); //產品出庫 40 pf.notifyAll(); //通知生產者可以繼續生產 41 } 42 try { 43 Thread.sleep(1000); //防止日誌列印過多,沒有可觀性 44 } catch (InterruptedException e) { 45 // TODO Auto-generated catch block 46 e.printStackTrace(); 47 } 48 } 49 } 50 } 51 52 }
2.2.5 開始工作
在main方法中,開啟消費者和生產者執行緒。
1 package com.dcits.weipt; 2 3 /** 4 * 公司領導 5 * 用於讓整個系統運作起來*/ 6 7 public class HelloThread{ 8 public static void main(String[] args) { 9 ProductFactory pf = new ProductFactory(); 10 Producer p = new Producer(pf); 11 Consumer c = new Consumer(pf); 12 Thread pt = new Thread(p); 13 Thread ct = new Thread(c); 14 pt.start(); //我們可以開啟多個消費者或者多個生產者 15 ct.start(); 16 } 17 }
到這裡,這篇文章就徹底結束了。
注:如需索要編譯好的專案原始碼可關注公眾號mht18391859179(掃描下方二維碼),回覆:2018091601 免費領取
如果需要交流或者指正,可通過上述公眾號,或者email:[email protected]與本人聯絡。
笨鳥先飛,終生學習
特別鳴謝:
1、感謝胡**,同志的交流與指導。
2、感謝部落格園,csdn,知乎等大牛文章的啟迪。