四、阻塞佇列的實現原理
在開發過程中,經常會使用到一些資料結構,如雜湊表(hashmap)、佇列(queue)等,但是有一部分資料結構是執行緒不安全的,即在併發場景下,資料的獲取和儲存會出現問題。
為了解決這種問題,jdk提供了幾個併發安全的資料結構,如下圖所示:
由上圖可知,jdk中提供的併發容器有:
concurrenthashmap
:分片加鎖實現高效能雜湊表。
concurrentlinkedqueue
:採用cas演算法實現的非阻塞佇列。
blockingqueue
:使用鎖物件實現的阻塞佇列。
實現乙個執行緒安全的佇列有兩種方式:
方式1:使用阻塞演算法,使用阻塞演算法的佇列可以用乙個鎖(入隊和出隊用同一把鎖)或兩個鎖(入隊和出隊用不同的鎖)等方式來實現。
方式2:使用非阻塞演算法 (cas)。非阻塞的實現方式是使用迴圈cas的方式來實現。
concurrenthashmap
、concurrentlinkedqueue
將單獨介紹。本文主要講解幾種阻塞佇列(blockingqueue
)的特點。
阻塞佇列(blockingqueue)是乙個支援兩個附加操作的佇列,這兩個操作支援阻塞的插入和移除方法。
支援阻塞的插入方法:當佇列滿時,佇列會阻塞插入元素的執行緒,直到佇列不滿的狀態。
支援阻塞的移除方法:當隊列為空時,獲取元素的執行緒會一致等待佇列變為非空的狀態。
阻塞佇列的應用場景:
阻塞佇列常用於生產者和消費者的場景,生產者是向佇列裡新增元素的執行緒,消費者是阻塞佇列內提供了4中 api 處理方式,如下圖所示:從佇列裡取元素的執行緒。阻塞佇列就是生產者用來存放元素、消費者用來獲取元素的容器。
丟擲異常:add(e)
:當佇列滿時,如果往佇列裡插入元素,會拋異常。
remove()
:當佇列空時,如果從佇列裡獲取元素,會丟擲異常。
element()
:當佇列空時,如果從佇列裡獲取元素,會丟擲異常。
返回特殊值:offer(e)
:當佇列滿時,如果往佇列裡插入元素,會返回 false 表示插入失敗。
poll()
:當佇列空時,如果從佇列裡獲取元素,會返回 null。
peek()
:當佇列空時,如果從佇列裡獲取元素,會返回 null。
一直阻塞:put(e)
:當佇列滿時,如果往佇列裡插入元素,佇列會阻塞當前執行緒,直到佇列可用或者響應中斷退出。
take()
:當佇列空時,如果從佇列裡獲取元素,佇列會阻塞當前執行緒,直到佇列不為空。
超時退出:offer(time, unit)
: 當佇列滿時,如果往佇列裡插入元素,佇列會阻塞當前執行緒一段時間,如果超過了指定的時間,當前執行緒就會退出。
poll(time, unit)
:當佇列空時,如果從佇列裡獲取元素,佇列會阻塞當前執行緒一段時間,如果超過了指定的時間,當前執行緒就會退出。
jdk1.7 提供了7個阻塞佇列:
arrayblockingqueue
:乙個由陣列結構組成的有界阻塞佇列。
linkedblockingqueue
:乙個由鍊錶結構組成的有界阻塞佇列。
priorityblockingqueue
:乙個支援優先順序排序的無界阻塞佇列。
delayqueue
:乙個使用優先順序佇列實現的無界阻塞佇列。
synchronousqueue
:乙個不儲存元素的阻塞佇列。
linkedtransferqueue
:乙個由鍊錶結構組成的無界阻塞佇列。
linkedblockingdeque
:乙個由鍊錶結構組成的雙向阻塞佇列。
arrayblockingqueue 是乙個用陣列實現的有界阻塞佇列。此佇列按照先進先出(fifo)的原則對元素進行排序。預設情況下使用非公平鎖進行佇列訪問 (公平鎖會降低吞吐量)。
linkedblockingqueue 是乙個用鍊錶實現的有界阻塞佇列。此佇列按照先進先出的原則對元素進行排序。此佇列的預設和最大長度為integer.max_value。
priorityblockingqueue 是乙個支援優先順序的無界阻塞佇列。預設情況下元素採取自然順序公升序排列。
支援自定義類實現compareto()方法來指定元素排序規則。
同優先順序元素,priorityblockingqueue不能保證其順序。
delayqueue 是乙個支援延時獲取元素的無界阻塞佇列。佇列使用priorityqueue來實現。delayqueue 的應用場景:佇列中的元素必須實現delayed介面,在建立元素時可以指定多久才能從佇列中獲取當前元素。
只有在延遲期滿時才能從佇列中提取元素。
快取系統的設計:可以用delayqueue儲存快取元素的有效期,使用乙個執行緒迴圈查詢delayqueue,一旦能從delayqueue中獲取元素時,表示快取有效期到了。定時任務排程:使用delayqueue儲存當天將會執行的任務和執行時間,一旦從delayqueue中獲取到任務就開始執行。
synchronousqueue 是乙個不儲存元素的阻塞佇列。優勢:每乙個put操作必須等待乙個take操作,否則不能繼續新增元素。
預設情況下執行緒採用非公平性策略訪問佇列。
synchronousqueue
的吞吐量高於linkedblockingqueue
和arrayblockingqueue
。
linkedtransferqueue 是乙個由鍊錶結構組成的無界阻塞transferqueue佇列。
linkedblockingdeque 是乙個由鍊錶結構組成的雙向阻塞佇列。如果佇列是空的,消費者會一直等待,當生產者新增元素時,消費者是如何知道當前佇列雙向佇列:指可以從佇列的兩端插入和移出元素。
雙向阻塞佇列可以運用在工作竊取模式中。
有元素的呢?
可以使用condition
介面的等待通知模式來實現。具體請參考:lock(五) — locksupport 和 condition介面
併發基礎 11 併發 容器
要實現乙個執行緒安全的佇列有兩個方式 一種是使用阻塞演算法,另一種是使用非阻塞演算法。阻塞演算法 使用阻塞演算法的佇列可以用乙個鎖 入隊和出隊同一把鎖 或兩把鎖 入隊和出隊用不同的鎖 來實現。非阻塞的實現方式則可以使用迴圈cas的方式來實現。concurrentlinkedqueue非阻塞執行緒安全...
併發程式設計9 併發容器
解決併發情況下的容器執行緒安全問題 譬如 vector,hashtable,都是給予同步鎖實現的 concurrent包下的類,大部分都是使用系統底層的實現,類似於native public class test09 latch.countdown for thread t arr try catc...
同步容器與併發容器
同步容器 可以簡單地理解為通過synchronized來實現同步的容器,如果有多個執行緒呼叫同步容器的方法,它們將會序列執行。比如vector,hashtable 早起jdk的一部分 及collections.synchronized 等方法返回的容器。可以通過檢視vector,hashtable等...