1. 程式人生 > >9.佇列:生產者消費者模式

9.佇列:生產者消費者模式

# 佇列:生產消費模式及執行緒池的運用 > 關注公眾號 MageByte,設定星標獲取最新干貨。 “加群” 進入技術交流群獲更多技術成長。 向固定大小的執行緒池投放請求任務時,若果執行緒池中沒有空閒資源了,這時候還有新的請求進來,執行緒池如何處理這個請求?拒絕請求還是排隊?使用怎樣的處理機制 一般兩種策略: - 直接拒絕任務請求; - 將請求排隊,等有空閒執行緒的時候取出排隊的請求繼續處理。 那如何儲存排隊的請求呢?這就是今天要講的話題。 其底層的資料結構就是今天我們要講的內容,「佇列」`Queue`。 完整程式碼詳見 GitHub:https://github.com/UniqueDong/algorithms.git ## 什麼是佇列 用一個生活例子,可以想象成超市排隊結賬,先來的先結賬,後面的人只能站在末尾,不允許插隊。**先進先出,這就是所謂的「佇列」** 佇列是一種線性資料結構,佇列的出口端叫「隊頭」,佇列的入口端叫「隊尾」。 與棧類似佇列的資料結構可以使用陣列實現也可以使用連結串列實現。關於棧的內容同學們可以翻閱歷史文章學習「[棧:實現瀏覽器前進後退](https://mp.weixin.qq.com/s?__biz=MzU3NDkwMjAyOQ==&mid=2247484033&idx=1&sn=16577ae7d8f5409f1355c7f41b874d5b&chksm=fd2a18e3ca5d91f5d971c2591fc56f7f2ecb8b906bd6b4e1e7300c45dc63969c2a32214b207b&token=1369939390&lang=zh_CN#rd)」,佇列最基本的操作也是兩個:**入隊 (enqueue)** ,將新元素放到隊尾;**出隊 (dequeue)**,從隊頭移除元素,出隊元素的下一個元素變成新的隊頭。 作為基礎的資料結構,佇列的應用也很廣泛,尤其是一些特定場景下的佇列。比如迴圈佇列、阻塞佇列、併發佇列。它們在很多偏底層系統、框架、中介軟體的開發中,起著關鍵性的作用。比如高效能佇列 Disruptor、Linux 環形快取,都用到了迴圈併發佇列;Java concurrent 併發包利用 ArrayBlockingQueue 來實現公平鎖等。 ![佇列與棧](https://magebyte.oss-cn-shenzhen.aliyuncs.com/演算法/image-20200504105200800.png) 佇列也是一種操作受限的線性表資料結構。 ## 順序佇列與鏈式佇列 佇列是跟棧一樣,是一種抽象的資料結構。 **具有先進先出的特性,在隊頭刪除資料,在隊尾插入資料。** 可以使用陣列實現,也可以使用連結串列實現。使用陣列實現的叫 **順序佇列**,用連結串列實現的 叫 **鏈式佇列**。 ### 順序佇列 一起先來看陣列實現的佇列: 1. 出隊操作就是把元素移除佇列,只允許在隊頭移除,出隊的下一個元素成為新的隊頭。 2. 入隊操作就是把新元素放入佇列,只允許在隊尾插入,新元素的的下一個位置成為隊尾。 **隨著不停地進行入隊、出隊操作,head 和 tail 都會持續往後移動。當 tail 移動到最右邊,即使陣列中還有空閒空間,也無法繼續往佇列中新增資料了。這個問題該如何解決呢?** 當出現這種情況的時候我們就需要做資料遷移。如圖所示:當 abcd 入隊後,對應的指標位置。 ![](https://magebyte.oss-cn-shenzhen.aliyuncs.com/演算法/20200504114436.png) 現在我們執行出隊操作 ![](https://magebyte.oss-cn-shenzhen.aliyuncs.com/演算法/20200504114630.png) 當我們呼叫兩次出隊操作之後,佇列中 head 指標指向下標為 2 的位置,tail 指標仍然指向下標為 4 的位置。 遷移操作其實就是把整段資料移動到陣列 0 開始的位置。 ![](https://magebyte.oss-cn-shenzhen.aliyuncs.com/演算法/20200504114729.png) 具體程式碼如下 ```java /** * 陣列實現佇列 */ public class Ar