依慣例,在說明生產者與消費者模型之前,先用一個現實的情境來舉例。
A 公司推出新的 3C 產品,預計會有兩、三波的搶購熱潮。
所以它找了三間代工廠,幫它預先生產了五萬組商品,放在集貨倉庫之中。後續持續生產三萬組。
如它預期,一上巿果然引起搶購熱潮,庫存在幾天內,就將商品售完。消費者因為沒有現貨,只能等待到貨。
A 公司評估銷售後,這情況,決定新增一間代工廠,追加兩萬組商品。
那知好景不長,B 公司推出殺手級的商品,大部份旳消費者轉向購買 B 公司的商品。
A 公司只能將緊急中止新代工廠的訂單,待原本代工廠完成原本的訂單後,停止生產。而己經生產的商品,持續放在倉庫中,等待消費者來購買。
在上面的故事中,我們看到三個角色。這三個角色動作的時間點與作用均不相同。
其中,代工廠作為生產者,負責產生資源(商品)。集貨倉庫可視為 Queue,負責資源的暫存緩衝與資源調派。消費者,就負責把資源取走、使用。
而且,在實例中,我們可以看到一些特性。
解耦
代工廠與消費者中間,透過集貨倉庫,將兩端隔離。不會發生消費者直接衝到代工廠內,大喊我要商品,快給我。或是代工廠衝到消費者面對,大喊給我買。
透過集貨倉庫,代工廠不會知道有那些消費者會來購買商品。相同的,消費者也不知道商品是那些代工廠所生產的。
A 公司決定更換代工廠,對消費者完全沒影響。消費者的身分,由個人購買更換為公司行號大量購買,對代工廠也沒有影響。因為兩者而言,它們所聚焦的重點在於商品本身,而非其他對象。
並發
以代工廠的角度,代工廠將生產出來的商品放到集貨倉庫,完全不需考慮商品的銷售情況。A 公司可以依商品的銷售情況,增減代工廠的數量。
以消費者的角度:如果商品本身很受歡迎,有購買意願的消費者數量就會增加。反之,如果沒有購買意願,消費者的數量就會減少。而不需理會代工廠產生的商品數量。
速率不均
在上面的例子中,因為商品生產的速度比較慢,所在代工廠在銷售之前,就預先生產五萬商品。但是因為消費者購買速率遠大於生產速度,造成售完的情況。
雖然,A 公司預估預產商品錯誤有誤,讓商品出現售完的情況。偶後,因為滯銷的情況,讓後面生商出來的商品只能存放於倉庫,等待慢慢銷售。
但換個角度,正是因為有集貨倉庫,讓商品可以預生產與滯銷的週轉空間。
將上面提到的三個特性,試著以軟體系統的角度解讀。
解耦
若將生產者與消費者分別視為兩個不同的 A、B 模組。
當發生 A 模組直接調用 B 模組的方法,兩者之間就建立起耦合關係。若將來 B 模組的方法變更,就可能影響到 A。
利用 Queue 將兩者隔離,讓兩者依賴於 Queue,A、B 模組之間沒有直接依賴,解決其耦合關係。
並發
系統可以依據最重要資源數量,動態決定是增加產生資源的生產者數量,還是減少處理資源的消費者數量。
速率不均
有時資料的產生,可能是一瞬間突然湧進,利用 Queue 的緩衝特性,讓來不及處理的資料可以先暫存下來,等後面產生資料的速度慢下來後,可以處理完成。
所以,Queue 的使用情境,可以彙整為以下幾點。
因為這種需要處理的問題,一再反覆出現。當其具有規則性旳部份,統整為一個模型,就是生產者與消費者模型(producer-consumers pattern)
同時並發、速率不均的因素,如何有效管理資源放入或取出的正確性,就變得十分的重要。為此,對 Queue 有以下的要求。
當符合兩點特性的 Queue ,稱為阻塞佇列 (BlockQueue) 。
接下來,介紹 Lock
、Semaphore
與 BlackingQueue
三種同步機制,確保 Queue 內的共用資源的正確性。