ArrayBlockingQueue 核心源碼分析

ArrayBlockingQueue 核心源碼分析

最聰明的人是最不願浪費時間的人。——但丁

0 前言

由數組支持的有界阻塞隊列。此隊列對元素按 FIFO(先進先出)進行排序。隊首是已在隊列中最長時間的元素。隊尾是最短時間出現在隊列中的元素。新元素插入到隊列的尾部,並且隊列檢索操作在隊列的開頭獲取元素。這是經典的“有界緩衝區”,其中固定大小的數組包含由生產者插入並由消費者提取的元素。一旦創建,容量將無法更改。試圖將一個元素放入一個完整的隊列將導致操作阻塞;從空隊列中取出一個元素的嘗試也會類似地阻塞。

此類支持可選的公平性策略,用於排序正在等待的生產者和使用者線程。默認情況下,不保證此排序。但是,將公平性設置為true構造的隊列將按FIFO順序授予線程訪問權限。公平通常會降低吞吐量,但會減少可變性並避免飢餓。

此類及其迭代器實現了Collection和Iterator接口的所有可選方法。

此類是Java Collections Framework的成員。

1 繼承體系

ArrayBlockingQueue 核心源碼分析

ArrayBlockingQueue 核心源碼分析

  • Java中的阻塞隊列接口BlockingQueue繼承自Queue接口。

2 屬性

  • 存儲隊列元素的數組,是個循環數組
  • 下次take, poll, peek or remove 時的數據索引
  • 下次 put, offer, or add 時的數據索引

有了上面兩個關鍵字段,在存數據和取數據時,無需計算,就能知道應該新增到什麼位置,應該從什麼位置取數據。

  • 隊列中的元素數

併發控制採用經典的雙條件(notEmpty + notFull)算法

  • Lock 鎖
  • 等待take的條件,在 put 成功時使用
  • 等待put的條件,在 take 成功時使用

ArrayBlockingQueue is a State-Dependent class,該類只有一些先決條件才能執行操作.

如果前提條件(notFull)為 false ,寫線程將只能等待.如果隊列滿,寫需要等待.原子式釋放鎖,並等待信號(讀線程發起的 notFull.signal())

ArrayBlockingQueue 核心源碼分析

對於讀,概念是相同的,但使用 notEmpty 條件:如果隊列為空,則讀線程需要等待.原子地釋放鎖,並等待信號(由寫線程觸發的 notEmpty.signal())

ArrayBlockingQueue 核心源碼分析

當一個線程被喚醒,那麼你需要做2件主要的事情:

  1. 獲取鎖
  2. 重測條件

這種設計,它支持只喚醒對剛剛發生的事情感興趣的線程.例如,一個試圖從空隊列中取數據的線程,只對隊列是否為空(有一些數據要取出)感興趣,而並不關心隊列是否滿。確實經典的設計!

3 構造方法

3.1 無參

注意這是沒有無參構造方法的哦!必須設置容量!

3.2 有參

  • 創建具有給定(固定)容量和默認訪問策略(非公平)的ArrayBlockingQueue
  • 創建具有給定(固定)容量和指定訪問策略的ArrayBlockingQueue
  • 創建一個具有給定(固定)容量,指定訪問策略並最初包含給定集合的元素的ArrayBlockingQueue,該元素以集合的迭代器的遍歷順序添加.

fair 參數

指定讀寫鎖是否公平

  • 公平鎖,鎖競爭按先來先到順序
  • 非公平鎖,鎖競爭隨機

3 新增數據

ArrayBlockingQueue有不同的幾個數據添加方法,add、offer、put方法,數據都會按照 putIndex 的位置新增.

3.1 add

  • 如果可以在不超過隊列容量的情況下立即將指定元素插入此隊列的尾部,則在成功插入時返回true,如果此隊列已滿則拋出IllegalStateException.
    調用的是抽象父類 AbstractQueue的 add 方法

offer

  • 之後又是調用的自身實現的 offer 方法.

enqueue

在當前放置位置插入元素,更新併發出信號.僅在持有鎖時可以調用

  • 內部繼續調用入隊方法

類似的看 put 方法.

3.2 put

  • 將指定的元素插入此隊列的末尾,如果隊列已滿,則等待空間變為可用.
    實現類似 add,不再贅述.

4 取數據

從隊首取數據,我們以 poll 為例看源碼.

4.1 poll

ArrayBlockingQueue 核心源碼分析

dequeue

  • 提取當前位置的元素,更新併發出信號.僅在持有鎖時可調用.

5 刪除數據

ArrayBlockingQueue 核心源碼分析

從源碼可以看出刪除有兩種情景:

  1. 刪除位置等於takeIndex,直接將該位元素置 null ,並重新計算 takeIndex
  2. 找到要刪除元素的下一個,計算刪除元素和 putIndex 的關係,若下一個元素是 putIndex,將 putIndex 的值修改成刪除位非 putIndex,將下一個元素往前移動一位

6 總結

ArrayBlockingQueue 是一種循環隊列,通過維護隊首、隊尾的指針,來優化插入、刪除,從而使時間複雜度為O(1).


分享到:


相關文章: