1. 程式人生 > 實用技巧 >阻塞佇列LinkedBlockingQueue原始碼學習

阻塞佇列LinkedBlockingQueue原始碼學習

前上一篇部落格學習了ArrayBlockingQueue,今天學習LinkedBlockingQueue原始碼並與之對比。

LinkedBlockingQueue總結

同樣先直接總結LinkedBlockingQueue相關的特性,再根據原始碼來進行說明,它的主要特性如下:

  1. 底層用連結串列實現資料儲存;
  2. 是一個FIFO無界阻塞佇列。佇列的最大容量預設是Intenger的最大值Integer.MAX_VALUE,也可以設定自定義容量;

重要屬性介紹

在這裡插入圖片描述
LinkedBlockingQueue有一個內部類 Node ,用來組成它儲存資料的連結串列,Node的 item 用來儲存資料, next 表示下一個節點,尾節點的next是null。

capacity 表示佇列最大容量,只能在初始化的時候設定,預設是Integer.MAX_VALUE;
count 表示當前儲存元素的數量,它是AtomicInteger型別;
head 連結串列的頭節點,它的item不存放資料;
tail 連結串列的尾節點,它的next為null,也就是沒有下一個節點;
takeLocknotEmpty 控制 take 方法的阻塞;
putLocknotFull 控制 put 方法的阻塞;

從上面屬性可以猜出來LinkedBlockingQueue是用兩個鎖來分別控制入隊和出隊的操作,這是因為LinkedBlockingQueue基於連結串列,出隊和入隊操作分別在隊頭和隊尾,理論上說並不衝突。若是像ArrayBlockingQueue一樣使用獨佔鎖,則 take 和 put 就不能真正併發。

也正是因為 take 和 put 鎖分離了,所以count有可能同時存在多個執行緒修改,有執行緒安全問題所以用 AtomicInteger 。

最關鍵的兩個私有方法enqueue和dequeue

在這裡插入圖片描述
LinkedBlockingQueue的 enqueuedequeue 方法比ArrayBlockingQueue的更加簡單;
enqueue方法就是把新的節點放到last的next,然後把新節點設定成尾節點。
dequeue是先拿到頭節點next節點作為first節點,然後頭節點的next指向了自己,這裡是我不太理解的地方,為什麼不乾脆將頭節點的next設定為null。
然後first節點就作為頭節點,把item拿出來後設置為null,所以頭節點是不儲存資料的,並且是由下一個節點升級來的。


dequeue的圖示如下
在這裡插入圖片描述

主要方法介紹

LinkedBlockingQueue與ArrayBlockingQueue一樣都是繼承至 AbstractQueue並實現 BlockingQueue介面,所以他們提供的方法都差不多,功能也類似,主要實現不同,上一篇已經詳細講了,這裡就只看一下takeput方法的實現。
在這裡插入圖片描述
在這裡插入圖片描述

與ArrayBlockingQueue對比

LinkedBlockingQueue與ArrayBlockingQueue是非常相似的類,通過對比能夠更體現他們的優缺點,主要對比如下:

  1. 從基礎屬性對比LinkedBlockingQueue底層實現是連結串列,ArrayBlockingQueue是陣列。
  2. ArrayBlockingQueue初始化需要一個數組,而LinkedBlockingQueue是動態數量的Node物件;所以ArrayBlockingQueue需要預先分配記憶體,而LinkedBlockingQueue不用,如果預計需要快取的資料很多的,那麼ArrayBlockingQueue一開始就需要很大一塊資料。
  3. ArrayBlockingQueue新增元素更快,因為它只是要要儲存的物件的引用放到陣列對應位置,而LinkedBlockingQueue需要建立一個Node物件;同時在獲取後這個Node物件變成了垃圾,在讀寫很大的情況會多出很多垃圾,可能會影響程式的效能;
  4. ArrayBlockingQueue用一個鎖控制讀寫,LinkedBlockingQueue用兩個鎖分別控制讀寫。因為ArrayBlockingQueue為了避免資料被覆蓋,而LinkedBlockingQueue不用擔心這種情況,可以同時支援讀寫,而ArrayBlockingQueue同一時刻支援一個操作,所以LinkedBlockingQueue吞吐量更高。
  5. ArrayBlockingQueue會佔用固定的記憶體,所以不能支援太大的快取,但是每次操作延遲更加低,而LinkedBlockingQueue不用暫用一個初始化的記憶體,但是每次操作消耗的記憶體更大,不過同時支援讀寫所以吞吐量更高。