生產者消費者模式最核心的部分是生產者與消費者之間的特殊容器,而阻塞佇列是特殊容器最常見的實作,JDK中定義了阻塞佇列介面BlockingQueue,JDK通過該介面為我們提供了很多種阻塞佇列的實作,其中包括本節的主角ArrayBlockingQueue,該類位于java.util.concurrent.ArrayBlockingQueue.java,該類需要實作的核心方法如下,下面我們詳細分析ArrayBlockingQueue的實作原理,
從名字可以看出它的存盤結構就是一個陣列,即基于陣列實作了一個FIFO的阻塞佇列,新元素都插入到佇列尾部,于是最先進入的元素在佇列頭而最后進入的元素在佇列尾部,該陣列是有界的,所以構造時需要制定陣列的大小,此外,該阻塞佇列還提供公平和非公平兩種模式,
使用例子
在分析 ArrayBlockingQueue的實作原理之前我們先通過幾個例子來了解它的使用,例子一中我們創建了一個 ArrayBlockingQueue物件,然后通過五個執行緒去生產資料并put到阻塞佇列,二主執行緒則充當消費端,不斷將阻塞佇列中的資料取出消費,程式輸出如下:
主要展示了阻塞佇列空間滿了產生阻塞,我們創建一個最大長度為4的阻塞佇列,然后通過五個執行緒分別產生一個資料put到阻塞佇列中,但由于阻塞佇列最大長度為4,所以在put四個資料后會產生阻塞,
主要展示了阻塞佇列為空時產生阻塞,我們創建一個最大長度為10的阻塞佇列,然后沒有生產者產生資料,卻又一個消費者呼叫take消費資料,由于阻塞佇列為空,所以產生了阻塞,
實作原理
我們先看 ArrayBlockingQueue類的屬性和建構式,其中count變數表示佇列的長度大小,takeIndex和putIndex分別表示佇列入隊和出隊的索引,items是一個Object陣列用于保存佇列的元素,lock是并發控制鎖,notEmpty和notFull分別表示佇列非空時和非滿時的條件,提供兩個建構式,我們在創建時指定佇列的大小,另外一個是公平模式引數,默認是非公平的,鎖物件使用的是ReentrantLock物件,公平機制也使用的是它的,它的公平機制我們在前面的章節中已經深入分析過了,notEmpty和notFull條件物件通過鎖物件的newCondition創建,
put和take方法是阻塞佇列的入隊和出隊方法,它們會間接呼叫enqueue和dequeue方法,其中put方法會檢查入隊的元素不能為null,需要先獲取鎖后才執行enqueue方法維護陣列,如果陣列長度已經達到最大長度則呼叫notFull條件的await方法等待,take方法會先獲取鎖后才執行dequeue方法維護陣列,如果陣列長度為0則呼叫notEmpty條件的await方法等待,
offer和poll方法則是支持超時的阻塞佇列的入隊和出隊方法,可以看到主要的區別就在于它們分別呼叫了notFull和notEmpty條件的awaitNanos方法進行等待,入隊時如果超過指定的超時時間則會回傳false,表示入隊超時導致失敗,而出隊時如果超過指定的超時時間則會回傳null,表示出隊超時導致失敗,
總結
圍繞著JDK中BlockingQueue的其中一種實作,即ArrayBlockingQueue,它的基本結構是使用陣列來保存阻塞佇列的元素,陣列的長度有限制,并且提供了公平和非公平模式,對它的實作原理進行分析,為了方便我們僅保留了ArrayBlockingQueue最核心的原始碼,只有我們能理解透徹這些內容就已經能掌握它的實作原理了,推薦閱讀
轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/249303.html
標籤:Java
