經過前面六篇博客的闡述,我想各位應該對阻塞隊列BlockingQueue有了較為深入的理解,下面來一個總結,先看整個類圖:
BlockingQueue
BlockingQueue接口實現Queue接口,它支持兩個附加操作:獲取元素時等待隊列變為非空,以及存儲元素時等待空間變得可用。相對於同一操作他提供了四種機制:拋出異常、返回特殊值、阻塞等待、超時:
BlockingQueue常用於生產者和消費者場景。
JDK 8 中提供了七個阻塞隊列可供使用(上圖的DelayedWorkQueue是ScheduledThreadPoolExecutor的內部類):
ArrayBlockingQueue :一個由數組結構組成的有界阻塞隊列。
LinkedBlockingQueue :一個由鏈表結構組成的無界阻塞隊列。
PriorityBlockingQueue :一個支持優先級排序的無界阻塞隊列。
DelayQueue:一個使用優先級隊列實現的無界阻塞隊列。
SynchronousQueue:一個不存儲元素的阻塞隊列。
LinkedTransferQueue:一個由鏈表結構組成的無界阻塞隊列。
LinkedBlockingDeque:一個由鏈表結構組成的雙向阻塞隊列。
ArrayBlockingQueue
基於數組的阻塞隊列,ArrayBlockingQueue內部維護這一個定長數組,阻塞隊列的大小在初始化時就已經確定了,其后無法更改。
采用可重入鎖ReentrantLock來保證線程安全性,但是生產者和消費者是共用同一個鎖對象,這樣勢必會導致降低一定的吞吐量。當然ArrayBlockingQueue完全可以采用分離鎖來實現生產者和消費者的並行操作,但是我認為這樣做只會給代碼帶來額外的復雜性,對於性能而言應該不會有太大的提升,因為基於數組的ArrayBlockingQueue在數據的寫入和讀取操作已經非常輕巧了。
ArrayBlockingQueue支持公平性和非公平性,默認采用非公平模式,可以通過構造函數設置為公平訪問策略(true)。
PriorityBlockingQueue
PriorityBlockingQueue是支持優先級的無界隊列。默認情況下采用自然順序排序,當然也可以通過自定義Comparator來指定元素的排序順序。
PriorityBlockingQueue內部采用二叉堆的實現方式,整個處理過程並不是特別復雜。添加操作則是不斷“上冒”,而刪除操作則是不斷“下掉”。
DelayQueue
DelayQueue是一個支持延時操作的無界阻塞隊列。列頭的元素是最先“到期”的元素,如果隊列里面沒有元素到期,是不能從列頭獲取元素的,哪怕有元素也不行。也就是說只有在延遲期滿時才能夠從隊列中去元素。
它主要運用於如下場景:
緩存系統的設計:緩存是有一定的時效性的,可以用DelayQueue保存緩存的有效期,然后利用一個線程查詢DelayQueue,如果取到元素就證明該緩存已經失效了。
定時任務的調度:DelayQueue保存當天將要執行的任務和執行時間,一旦取到元素(任務),就執行該任務。
DelayQueue采用支持優先級的PriorityQueue來實現,但是隊列中的元素必須要實現Delayed接口,Delayed接口用來標記那些應該在給定延遲時間之后執行的對象,該接口提供了getDelay()方法返回元素節點的剩余時間。同時,元素也必須要實現compareTo()方法,compareTo()方法需要提供與getDelay()方法一致的排序。
SynchronousQueue
SynchronousQueue是一個神奇的隊列,他是一個不存儲元素的阻塞隊列,也就是說他的每一個put操作都需要等待一個take操作,否則就不能繼續添加元素了,有點兒像Exchanger,類似於生產者和消費者進行交換。
隊列本身不存儲任何元素,所以非常適用於傳遞性場景,兩者直接進行對接。其吞吐量會高於ArrayBlockingQueue和LinkedBlockingQueue。
SynchronousQueue支持公平和非公平的訪問策略,在默認情況下采用非公平性,也可以通過構造函數來設置為公平性。
SynchronousQueue的實現核心為Transferer接口,該接口有TransferQueue和TransferStack兩個實現類,分別對應着公平策略和非公平策略。接口Transferer有一個tranfer()方法,該方法定義了轉移數據,如果e != null,相當於將一個數據交給消費者,如果e == null,則相當於從一個生產者接收一個消費者交出的數據。
LinkedTransferQueue
LinkedTransferQueue是一個由鏈表組成的的無界阻塞隊列,該隊列是一個相當牛逼的隊列:它是ConcurrentLinkedQueue、SynchronousQueue (公平模式下)、無界的LinkedBlockingQueues等的超集。
與其他BlockingQueue相比,他多實現了一個接口TransferQueue,該接口是對BlockingQueue的一種補充,多了tryTranfer()和transfer()兩類方法:
tranfer():若當前存在一個正在等待獲取的消費者線程,即立刻移交之。 否則,會插入當前元素e到隊列尾部,並且等待進入阻塞狀態,到有消費者線程取走該元素
tryTranfer(): 若當前存在一個正在等待獲取的消費者線程(使用take()或者poll()函數),使用該方法會即刻轉移/傳輸對象元素e;若不存在,則返回false,並且不進入隊列。這是一個不阻塞的操作
LinkedBlockingDeque
LinkedBlockingDeque是一個有鏈表組成的雙向阻塞隊列,與前面的阻塞隊列相比它支持從兩端插入和移出元素。以first結尾的表示從對頭操作,以last結尾的表示從對尾操作。
在初始化LinkedBlockingDeque時可以初始化隊列的容量,用來防止其再擴容時過渡膨脹。另外雙向阻塞隊列可以運用在“工作竊取”模式中。
擴展閱讀
歡迎掃一掃我的公眾號關注 — 及時得到博客訂閱哦!
–— Java成神之路: 488391811(一起走向Java成神) –—