1. 程式人生 > >結合生活,剖析《生產者消費者模型》-java多執行緒(一)

結合生活,剖析《生產者消費者模型》-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,知乎等大牛文章的啟迪。