Java多線程系列---「基礎篇」11之 生產消費者問題

轉自:http://www.cnblogs.com/skywang12345/p/3480016.htmlhtml

概要

本章,會對「生產/消費者問題」進行討論。涉及到的內容包括:
1. 生產/消費者模型
2. 生產/消費者實現java

1. 生產/消費者模型

生產/消費者問題是個很是典型的多線程問題,涉及到的對象包括「生產者」、「消費者」、「倉庫」和「產品」。他們之間的關係以下:
(01) 生產者僅僅在倉儲未滿時候生產,倉滿則中止生產。
(02) 消費者僅僅在倉儲有產品時候才能消費,倉空則等待。
(03) 當消費者發現倉儲沒產品可消費時候會通知生產者生產。
(04) 生產者在生產出可消費產品時候,應該通知等待的消費者去消費。多線程

 

2. 生產/消費者實現

下面經過wait()/notify()方式實現該模型(後面在學習了線程池相關內容以後,再經過其它方式實現生產/消費者模型)。源碼以下:學習

// Demo1.java // 倉庫
class Depot { private int capacity;    // 倉庫的容量
    private int size;        // 倉庫的實際數量

    public Depot(int capacity) { this.capacity = capacity; this.size = 0; } public synchronized void produce(int val) { try { // left 表示「想要生產的數量」(有可能生產量太多,需多今生產)
            int left = val; while (left > 0) { // 庫存已滿時,等待「消費者」消費產品。
                while (size >= capacity) wait(); // 獲取「實際生產的數量」(即庫存中新增的數量) // 若是「庫存」+「想要生產的數量」>「總的容量」,則「實際增量」=「總的容量」-「當前容量」。(此時填滿倉庫) // 不然「實際增量」=「想要生產的數量」
                int inc = (size+left)>capacity ? (capacity-size) : left; size += inc; left -= inc; System.out.printf("%s produce(%3d) --> left=%3d, inc=%3d, size=%3d\n", Thread.currentThread().getName(), val, left, inc, size); // 通知「消費者」能夠消費了。
 notifyAll(); } } catch (InterruptedException e) { } } public synchronized void consume(int val) { try { // left 表示「客戶要消費數量」(有可能消費量太大,庫存不夠,需多此消費)
            int left = val; while (left > 0) { // 庫存爲0時,等待「生產者」生產產品。
                while (size <= 0) wait(); // 獲取「實際消費的數量」(即庫存中實際減小的數量) // 若是「庫存」<「客戶要消費的數量」,則「實際消費量」=「庫存」; // 不然,「實際消費量」=「客戶要消費的數量」。
                int dec = (size<left) ? size : left; size -= dec; left -= dec; System.out.printf("%s consume(%3d) <-- left=%3d, dec=%3d, size=%3d\n", Thread.currentThread().getName(), val, left, dec, size); notifyAll(); } } catch (InterruptedException e) { } } public String toString() { return "capacity:"+capacity+", actual size:"+size; } } // 生產者
class Producer { private Depot depot; public Producer(Depot depot) { this.depot = depot; } // 消費產品:新建一個線程向倉庫中生產產品。
    public void produce(final int val) { new Thread() { public void run() { depot.produce(val); } }.start(); } } // 消費者
class Customer { private Depot depot; public Customer(Depot depot) { this.depot = depot; } // 消費產品:新建一個線程從倉庫中消費產品。
    public void consume(final int val) { new Thread() { public void run() { depot.consume(val); } }.start(); } } public class Demo1 { public static void main(String[] args) { Depot mDepot = new Depot(100); Producer mPro = new Producer(mDepot); Customer mCus = new Customer(mDepot); mPro.produce(60); mPro.produce(120); mCus.consume(90); mCus.consume(150); mPro.produce(110); } }

 

說明
(01) Producer是「生產者」類,它與「倉庫(depot)」關聯。當調用「生產者」的produce()方法時,它會新建一個線程並向「倉庫」中生產產品。
(02) Customer是「消費者」類,它與「倉庫(depot)」關聯。當調用「消費者」的consume()方法時,它會新建一個線程並消費「倉庫」中的產品。
(03) Depot是「倉庫」類,倉庫中記錄「倉庫的容量(capacity)」以及「倉庫中當前產品數目(size)」。
        「倉庫」類的生產方法produce()和消費方法consume()方法都是synchronized方法,進入synchronized方法體,意味着這個線程獲取到了該「倉庫」對象的同步鎖。這也就是說,同一時間,生產者和消費者線程只能有一個能運行。經過同步鎖,實現了對「殘酷」的互斥訪問。
       對於生產方法produce()而言:當倉庫滿時,生產者線程等待,須要等待消費者消費產品以後,生產線程才能生產;生產者線程生產完產品以後,會經過notifyAll()喚醒同步鎖上的全部線程,包括「消費者線程」,即咱們所說的「通知消費者進行消費」。
      對於消費方法consume()而言:當倉庫爲空時,消費者線程等待,須要等待生產者生產產品以後,消費者線程才能消費;消費者線程消費完產品以後,會經過notifyAll()喚醒同步鎖上的全部線程,包括「生產者線程」,即咱們所說的「通知生產者進行生產」。this

(某一次)運行結果spa

複製代碼
Thread-0 produce( 60) --> left=  0, inc= 60, size= 60
Thread-4 produce(110) --> left= 70, inc= 40, size=100
Thread-2 consume( 90) <-- left=  0, dec= 90, size= 10
Thread-3 consume(150) <-- left=140, dec= 10, size=  0
Thread-1 produce(120) --> left= 20, inc=100, size=100
Thread-3 consume(150) <-- left= 40, dec=100, size=  0
Thread-4 produce(110) --> left=  0, inc= 70, size= 70
Thread-3 consume(150) <-- left=  0, dec= 40, size= 30
Thread-1 produce(120) --> left=  0, inc= 20, size= 50
複製代碼
相關文章
相關標籤/搜索