接ArrayBlockingQueue,這里我談下我對LinkedBlockingQueue的理解。
由阻塞隊列的ArrayBlockingQueue和LinkedBlockingQueue,我們很容易聯想到List的兩種實現:ArrayList和LinkedList。ArrayList與ArrayBlockingQueue相似,底層都使用了數組,同樣LinkedList和LinkedBlockingQueue也類似,底層使用了鏈表。不過這里的鏈表還是有些許不同的:
1. LinkedList使用了雙向鏈表,而LinkedBlockingQueue使用了單向鏈表。
2. LinkedList的容量理論上是無限的,而LinkedBlockingQueue卻是有限的。
讓我們來看看構造函數:
無參構造函數實際上創建了一個大小為Integer.MAX_VALUE,而有參構造函數傳入的也是int。也就是說其實LinkedBlockingQueue是有界的。
接下來,我們看看它所使用的鎖。
從代碼中可以看出,它有兩個鎖,一個takeLock(讀鎖),一個putLock(寫鎖)。這與ArrayBlockingQueue是不一樣的。ArrayBlockingQueue只有一個鎖。為什么有這樣的差異呢?ArrayBlockingQueue底層使用了數組,存儲空間是連續的,讀寫同一片存儲空間,而且可能涉及到元素的移動,只能用同一把鎖來控制訪問,而LinkedBlockingQueue使用的是鏈表,存儲空間是不連續的,元素的異動只是指針的變更,讀寫鎖自然可以分離。
兩個Condition分別使用在不同場景下:
notEmpty和takeLock搭配,獲取了takeLock還不夠,還必須滿足notEmpty的條件。
notFull和putLock搭配,獲取了putLock還不夠,還必須滿足notFull的條件。
再來看看put方法。
代碼比較簡單明了,只說三點:
1. 不允許Null值存入鏈表。
2. 入隊enqueue方法有兩步,第一,把尾指針的next指向新加入的Node,第二,把尾指針重新指向新加入的Node。這個方法是線程安全的,因為它是在獲得lock后執行的
3. 局部變量c是從-1開始的,代表元素還沒有成功設置。
類似的,我們也可以分析take方法,這里就不在贅述。可以稍微看下出隊的方法。
首先,把頭結點的next指向自己,這樣就沒有GC Roots可達了,接著把第一個節點元素置空,好騰出來做頭結點。最后返回置空前的元素值。
LinkedBlockingQueue就分析到這里。