Java核心知識 多線程併發 阻塞隊列原理(二十四)

Java核心知識 多線程併發 阻塞隊列原理(二十四)

阻塞隊列,關鍵字是阻塞,先理解阻塞的含義,在阻塞隊列中,線程阻塞有這樣的兩種情況:

1). 當隊列中沒有數據的情況下,消費者端的所有線程都會被自動阻塞(掛起),直到有數據放 入隊列。


Java核心知識 多線程併發 阻塞隊列原理(二十四)

2). 當隊列中填滿數據的情況下,生產者端的所有線程都會被自動阻塞(掛起),直到隊列中有 空的位置,線程被自動喚醒。


Java核心知識 多線程併發 阻塞隊列原理(二十四)

1. 阻塞隊列的主要方法


Java核心知識 多線程併發 阻塞隊列原理(二十四)

 拋出異常:拋出一個異常;

 特殊值:返回一個特殊值(null 或 false,視情況而定)

 則塞:在成功操作之前,一直阻塞線程

 超時:放棄前只在最大的時間內阻塞

插入操作:

1):public abstract boolean add(E paramE):將指定元素插入此隊列中(如果立即可行 且不會違反容量限制),成功時返回 true,如果當前沒有可用的空間,則拋

出 IllegalStateException。如果該元素是 NULL,則會拋出 NullPointerException 異常。

2):public abstract boolean offer(E paramE):將指定元素插入此隊列中(如果立即可行 且不會違反容量限制),成功時返回 true,如果當前沒有可用的空間,則返回 false。

3):public abstract void put(E paramE) throws InterruptedException: 將指定元素插 入此隊列中,將等待可用的空間(如果有必要)

public void put(E paramE) throws InterruptedException {

checkNotNull(paramE);

ReentrantLock localReentrantLock = this.lock;

localReentrantLock.lockInterruptibly();

try {

while (this.count == this.items.length)

this.notFull.await();//如果隊列滿了,則線程阻塞等待

enqueue(paramE);

localReentrantLock.unlock(); } finally {

localReentrantLock.unlock(); }

}

4):offer(E o, long timeout, TimeUnit unit):可以設定等待的時間,如果在指定的時間 內,還不能往隊列中加入 BlockingQueue,則返回失敗。

獲取數據操作:

1):poll(time):取走 BlockingQueue 裡排在首位的對象,若不能立即取出,則可以等 time 參數 規定的時間,取不到時返回 null;

2):poll(long timeout, TimeUnit unit):從 BlockingQueue 取出一個隊首的對象,如果在

指定時間內,隊列一旦有數據可取,則立即返回隊列中的數據。否則直到時間超時還沒有數 據可取,返回失敗。

3):take():取走 BlockingQueue 裡排在首位的對象,若 BlockingQueue 為空,阻斷進入等待狀 態直到 BlockingQueue 有新的數據被加入。

4).drainTo():一次性從 BlockingQueue 獲取所有可用的數據對象(還可以指定獲取數據的個 數),通過該方法,可以提升獲取數據效率;不需要多次分批加鎖或釋放鎖。

2. Java 中的阻塞隊列

1). ArrayBlockingQueue :由數組結構組成的有界阻塞隊列。

2). LinkedBlockingQueue :由鏈表結構組成的有界阻塞隊列。

3). PriorityBlockingQueue :支持優先級排序的無界阻塞隊列。

4). DelayQueue:使用優先級隊列實現的無界阻塞隊列。

5). SynchronousQueue:不存儲元素的阻塞隊列。

6). LinkedTransferQueue:由鏈表結構組成的無界阻塞隊列。

7). LinkedBlockingDeque:由鏈表結構組成的雙向阻塞隊列


Java核心知識 多線程併發 阻塞隊列原理(二十四)

3. ArrayBlockingQueue(公平、非公平)

用數組實現的有界阻塞隊列。此隊列按照先進先出(FIFO)的原則對元素進行排序。默認情況下 不保證訪問者公平的訪問隊列,所謂公平訪問隊列是指阻塞的所有生產者線程或消費者線程,當 隊列可用時,可以按照阻塞的先後順序訪問隊列,即先阻塞的生產者線程,可以先往隊列裡插入 元素,先阻塞的消費者線程,可以先從隊列裡獲取元素。通常情況下為了保證公平性會降低吞吐 量。我們可以使用以下代碼創建一個公平的阻塞隊列:

ArrayBlockingQueue fairQueue = new ArrayBlockingQueue(1000,true);

4. LinkedBlockingQueue(兩個獨立鎖提高併發)

基於鏈表的阻塞隊列,同 ArrayListBlockingQueue 類似,此隊列按照先進先出(FIFO)的原則對 元素進行排序。而 LinkedBlockingQueue 之所以能夠高效的處理併發數據,還因為其對於生產者 端和消費者端分別採用了獨立的鎖來控制數據同步,這也意味著在高併發的情況下生產者和消費 者可以並行地操作隊列中的數據,以此來提高整個隊列的併發性能。

LinkedBlockingQueue 會默認一個類似無限大小的容量(Integer.MAX_VALUE)。

5. PriorityBlockingQueue(compareTo 排序實現優先)

是一個支 持 優 先 級 的 無 界 隊 列 。 默 認 情 況 下 元 素 採 取 自 然 順 序 升 序 排 列 。 可 以 自 定 義 實 現 compareTo()方法來指定元素進行排序規則,或者初始化 PriorityBlockingQueue 時,指定構造 參數 Comparator 來對元素進行排序。需要注意的是不能保證同優先級元素的順序。

6. DelayQueue(緩存失效、定時任務 )

是一個支持延時獲取元素的無界阻塞隊列。隊列使用 PriorityQueue 來實現。隊列中的元素必須實 現 Delayed 接口,在創建元素時可以指定多久才能從隊列中獲取當前元素。只有在延遲期滿時才 能從隊列中提取元素。我們可以將 DelayQueue 運用在以下應用場景:

1). 緩 存系 統的設 計: 可以用 DelayQueue 保 存緩存 元素 的有效 期, 使用一 個線 程循環 查詢 DelayQueue,一旦能從 DelayQueue 中獲取元素時,表示緩存有效期到了。

2). 定時任務調度:使用 DelayQueue 保存當天將會執行的任務和執行時間,一旦從 DelayQueue 中獲取到任務就開始執行,從比如 TimerQueue 就是使用 DelayQueue 實現的。

7. SynchronousQueue(不存儲數據、可用於傳遞數據)

是一個不存儲元素的阻塞隊列。每一個 put 操作必須等待一個 take 操作,否則不能繼續添加元素。 SynchronousQueue 可以看成是一個傳球手,負責把生產者線程處理的數據直接傳遞給消費者線 程。隊列本身並不存儲任何元素,非常適合於傳遞性場景,比如在一個線程中使用的數據,傳遞給 另 外 一 個 線 程 使 用 , SynchronousQueue 的 吞 吐 量 高 於 LinkedBlockingQueue 和 ArrayBlockingQueue。

8. LinkedTransferQueue

是 一 個 由 鏈 表 結 構 組 成 的 無 界 阻 塞 TransferQueue 隊 列 。 相 對 於 其 他 阻 塞 隊 列 , LinkedTransferQueue 多了 tryTransfer 和 transfer 方法。

1). transfer 方法:如果當前有消費者正在等待接收元素(消費者使用 take()方法或帶時間限制的 poll()方法時),transfer 方法可以把生產者傳入的元素立刻 transfer(傳輸)給消費者。如 果沒有消費者在等待接收元素,transfer 方法會將元素存放在隊列的 tail 節點,並等到該元素 被消費者消費了才返回。

2). tryTransfer 方法。則是用來試探下生產者傳入的元素是否能直接傳給消費者。如果沒有消費 者等待接收元素,則返回 false。和 transfer 方法的區別是 tryTransfer 方法無論消費者是否 接收,方法立即返回。而 transfer 方法是必須等到消費者消費了才返回。

對於帶有時間限制的 tryTransfer(E e, long timeout, TimeUnit unit)方法,則是試圖把生產者傳 入的元素直接傳給消費者,但是如果沒有消費者消費該元素則等待指定的時間再返回,如果超時 還沒消費元素,則返回 false,如果在超時時間內消費了元素,則返回 true。

9. LinkedBlockingDeque

是一個由鏈表結構組成的雙向阻塞隊列。所謂雙向隊列指的你可以從隊列的兩端插入和移出元素。 雙端隊列因為多了一個操作隊列的入口,在多線程同時入隊時,也就減少了一半的競爭。相比其 他 的 阻 塞 隊 列 , LinkedBlockingDeque 多了 addFirst , addLast , offerFirst , offerLast, peekFirst,peekLast 等方法,以 First 單詞結尾的方法,表示插入,獲取(peek)或移除雙端隊 列的第一個元素。以 Last 單詞結尾的方法,表示插入,獲取或移除雙端隊列的最後一個元素。另 外插入方法 add 等同於 addLast,移除方法 remove 等效於 removeFirst。但是 take 方法卻等同 於 takeFirst,不知道是不是 Jdk 的 bug,使用時還是用帶有 First 和 Last 後綴的方法更清楚。

在初始化 LinkedBlockingDeque 時可以設置容量防止其過渡膨脹。另外雙向阻塞隊列可以運用在 “工作竊取”模式中。


分享到:


相關文章: