生產者與消費者模式,數組阻塞隊列(ArrayBlockingQueue)

{"type":"doc","content":[{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"生產者消費者模式最核心的部分是生產者與消費者之間的特殊容器,而阻塞隊列是特殊容器最常見的實現。JDK中定義了阻塞隊列接口BlockingQueue,JDK通過該接口爲我們提供了很多種阻塞隊列的實現,其中包括本節的主角ArrayBlockingQueue,該類位於java.util.concurrent.ArrayBlockingQueue.java。該類需要實現的核心方法如下,下面我們詳細分析ArrayBlockingQueue的實現原理。","attrs":{}}]},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/ba/baa203a1a053acf83b39a72cf6e4856f.webp","alt":null,"title":"","style":[{"key":"width","value":"100%"},{"key":"bordertype","value":"none"}],"href":"","fromPaste":false,"pastePass":false}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"從名字可以看出它的存儲結構就是一個數組,即基於數組實現了一個FIFO的阻塞隊列。新元素都插入到隊列尾部,於是最先進入的元素在隊列頭而最後進入的元素在隊列尾部。該數組是有界的,所以構造時需要制定數組的大小。此外,該阻塞隊列還提供公平和非公平兩種模式。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/25/25b39b05d702217e44f299316d229cb2.webp","alt":"圖片","title":null,"style":[{"key":"width","value":"75%"},{"key":"bordertype","value":"none"}],"href":null,"fromPaste":true,"pastePass":true}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"heading","attrs":{"align":null,"level":1},"content":[{"type":"text","text":"使用例子","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"在分析 ArrayBlockingQueue的實現原理之前我們先通過幾個例子來了解它的使用。例子一中我們創建了一個 ArrayBlockingQueue對象,然後通過五個線程去生產數據並put到阻塞隊列,二主線程則充當消費端,不斷將阻塞隊列中的數據取出消費。程序輸出如下:","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/7e/7e5b7de8da4594754f126c7307052341.webp","alt":null,"title":"","style":[{"key":"width","value":"100%"},{"key":"bordertype","value":"none"}],"href":"","fromPaste":false,"pastePass":false}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"主要展示了阻塞隊列空間滿了產生阻塞,我們創建一個最大長度爲4的阻塞隊列,然後通過五個線程分別產生一個數據put到阻塞隊列中,但由於阻塞隊列最大長度爲4,所以在put四個數據後會產生阻塞。","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/60/60ca7959993f24e1f8df93d17ea59b57.webp","alt":null,"title":"","style":[{"key":"width","value":"100%"},{"key":"bordertype","value":"none"}],"href":"","fromPaste":false,"pastePass":false}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"主要展示了阻塞隊列爲空時產生阻塞,我們創建一個最大長度爲10的阻塞隊列,然後沒有生產者產生數據,卻又一個消費者調用take消費數據。由於阻塞隊列爲空,所以產生了阻塞。","attrs":{}}]},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/7c/7cf4fc04d9bdab2aeb3af72b6ac9817f.webp","alt":null,"title":"","style":[{"key":"width","value":"100%"},{"key":"bordertype","value":"none"}],"href":"","fromPaste":false,"pastePass":false}},{"type":"heading","attrs":{"align":null,"level":1},"content":[{"type":"text","text":"實現原理","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"我們先看 ArrayBlockingQueue類的屬性和構造函數。其中count變量表示隊列的長度大小,takeIndex和putIndex分別表示隊列入隊和出隊的索引,items是一個Object數組用於保存隊列的元素,lock是併發控制鎖,notEmpty和notFull分別表示隊列非空時和非滿時的條件。提供兩個構造函數,我們在創建時指定隊列的大小,另外一個是公平模式參數,默認是非公平的。鎖對象使用的是ReentrantLock對象,公平機制也使用的是它的,它的公平機制我們在前面的章節中已經深入分析過了。notEmpty和notFull條件對象通過鎖對象的newCondition創建。","attrs":{}}]},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/2d/2da4d27889c83eb5856905549ca66640.webp","alt":null,"title":"","style":[{"key":"width","value":"100%"},{"key":"bordertype","value":"none"}],"href":"","fromPaste":false,"pastePass":false}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"put和take方法是阻塞隊列的入隊和出隊方法,它們會間接調用enqueue和dequeue方法。其中put方法會檢查入隊的元素不能爲null,需要先獲取鎖後才執行enqueue方法維護數組,如果數組長度已經達到最大長度則調用notFull條件的await方法等待。take方法會先獲取鎖後才執行dequeue方法維護數組,如果數組長度爲0則調用notEmpty條件的await方法等待。","attrs":{}}]},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/ae/ae327a1352a564a08b2898b930db9803.webp","alt":null,"title":"","style":[{"key":"width","value":"100%"},{"key":"bordertype","value":"none"}],"href":"","fromPaste":false,"pastePass":false}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"offer和poll方法則是支持超時的阻塞隊列的入隊和出隊方法,可以看到主要的區別就在於它們分別調用了notFull和notEmpty條件的awaitNanos方法進行等待。入隊時如果超過指定的超時時間則會返回false,表示入隊超時導致失敗。而出隊時如果超過指定的超時時間則會返回null,表示出隊超時導致失敗。","attrs":{}}]},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/a1/a19d83ead1fec772f416caf0c5698a04.webp","alt":null,"title":"","style":[{"key":"width","value":"100%"},{"key":"bordertype","value":"none"}],"href":"","fromPaste":false,"pastePass":false}},{"type":"heading","attrs":{"align":null,"level":1},"content":[{"type":"text","text":"總結 ","attrs":{}}]},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null},"content":[{"type":"text","text":"圍繞着JDK中BlockingQueue的其中一種實現,即ArrayBlockingQueue。它的基本結構是使用數組來保存阻塞隊列的元素,數組的長度有限制,並且提供了公平和非公平模式。對它的實現原理進行分析,爲了方便我們僅保留了ArrayBlockingQueue最核心的源碼,只有我們能理解透徹這些內容就已經能掌握它的實現原理了。推薦閱讀","attrs":{}}]},{"type":"image","attrs":{"src":"https://static001.geekbang.org/infoq/0f/0fccb26755ddfd8a6663e05533f0272d.webp","alt":null,"title":"","style":[{"key":"width","value":"100%"},{"key":"bordertype","value":"none"}],"href":"","fromPaste":false,"pastePass":false}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}},{"type":"paragraph","attrs":{"indent":0,"number":0,"align":null,"origin":null}}]}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章