19.并发容器之BlockingQueue
在實際編程中,會經常使用到JDK中Collection集合框架中的各種容器類如實現List,Map,Queue接口的容器類,但是這些容器類基本上不是線程安全的,除了使用Collections可以將其轉換為線程安全的容器,Doug Lea大師為我們都準備了對應的線程安全的容器,如實現List接口的CopyOnWriteArrayList(關于CopyOnWriteArrayList可以看這篇文章),實現Map接口的ConcurrentHashMap(關于ConcurrentHashMap可以看這篇文章),實現Queue接口的ConcurrentLinkedQueue(關于ConcurrentLinkedQueue可以看這篇文章)。
最常用的"
2. 基本操作
BlockingQueue基本操作總結如下(此圖來源于JAVA API文檔):
BlockingQueue繼承于Queue接口,因此,對數據元素的基本操作有:
插入元素
add(E e) :往隊列插入數據,當隊列滿時,插入元素時會拋出IllegalStateException異常;
offer(E e):當往隊列插入數據時,插入成功返回true,否則則返回false。當隊列滿時不會拋出異常;
刪除元素
remove(Object o):從隊列中刪除數據,成功則返回true,否則為false
poll:刪除數據,當隊列為空時,返回null;
查看元素
element:獲取隊頭元素,如果隊列為空時則拋出NoSuchElementException異常;
peek:獲取隊頭元素,如果隊列為空則拋出NoSuchElementException異常
BlockingQueue具有的特殊操作:
插入數據:
put():當阻塞隊列容量已經滿時,往阻塞隊列插入數據的線程會被阻塞,直至阻塞隊列已經有空余的容量可供使用;
offer(E e, long timeout, TimeUnit unit):若阻塞隊列已經滿時,同樣會阻塞插入數據的線程,直至阻塞隊列已經有空余的地方,與put方法不同的是,該方法會有一個超時時間,若超過當前給定的超時時間,插入數據的線程會退出;
刪除數據
take():當阻塞隊列為空時,獲取隊頭數據的線程會被阻塞;
poll(long timeout, TimeUnit unit):當阻塞隊列為空時,獲取數據的線程會被阻塞,另外,如果被阻塞的線程超過了給定的時長,該線程會退出
3. 常用的BlockingQueue
實現BlockingQueue接口的有ArrayBlockingQueue, LinkedBlockingQueue , PriorityBlockingQueue, DelayQueue,LinkedTransferQueue, SynchronousQueue, LinkedBlockingDeque,而這幾種常見的阻塞隊列也是在實際編程中會常用的,下面對這幾種常見的阻塞隊列進行說明:
1.ArrayBlockingQueue
ArrayBlockingQueue是由數組實現的有界阻塞隊列。該隊列命令元素FIFO(先進先出)。因此,隊頭元素保存的是隊列中存在時間最長的數據元素,而隊尾數據保存的則是當前隊列最新的數據元素。ArrayBlockingQueue可作為“有界數據緩沖區”,生產者插入數據到隊列容器中,并由消費者提取。ArrayBlockingQueue一旦創建,容量不能改變。
當隊列容量滿時,嘗試將元素放入隊列將導致操作阻塞;嘗試從一個空隊列中取一個元素也會同樣阻塞。
ArrayBlockingQueue默認情況下不能保證線程訪問隊列的公平性,所謂公平性是指嚴格按照線程等待的絕對時間順序,即最先等待的線程能夠最先訪問到ArrayBlockingQueue。而非公平性則是指訪問ArrayBlockingQueue的順序不是遵守嚴格的時間順序,有可能存在,一旦ArrayBlockingQueue可以被訪問時,長時間阻塞的線程依然無法訪問到ArrayBlockingQueue。如果保證公平性,通常會降低吞吐量。如果需要獲得公平性的ArrayBlockingQueue,可采用如下代碼:
private static ArrayBlockingQueue<Integer> blockingQueue = new ArrayBlockingQueue<Integer>(10,true);關于ArrayBlockingQueue的實現原理,可以看這篇文章。
2.LinkedBlockingQueue
LinkedBlockingQueue是用鏈表實現的有界阻塞隊列,同樣滿足FIFO的特性,與ArrayBlockingQueue相比起來具有更高的吞吐量,為了防止LinkedBlockingQueue容量迅速增,損耗大量內存。通常在創建LinkedBlockingQueue對象時,會指定其大小,如果未指定,容量等于Integer.MAX_VALUE
3.PriorityBlockingQueue
PriorityBlockingQueue是一個支持優先級的無界阻塞隊列。默認情況下元素采用自然順序進行排序,也可以通過自定義類實現compareTo()方法來指定元素排序規則,或者初始化時通過構造器參數Comparator來指定排序規則。
4.SynchronousQueue
SynchronousQueue每個插入操作必須等待另一個線程進行相應的刪除操作,因此,SynchronousQueue實際上沒有存儲任何數據元素,因為只有線程在刪除數據時,其他線程才能插入數據,同樣的,如果當前有線程在插入數據時,線程才能刪除數據。SynchronousQueue也可以通過構造器參數來為其指定公平性。
5.LinkedTransferQueue
LinkedTransferQueue是一個由鏈表數據結構構成的無界阻塞隊列,由于該隊列實現了TransferQueue接口,與其他阻塞隊列相比主要有以下不同的方法:
transfer(E e) 如果當前有線程(消費者)正在調用take()方法或者可延時的poll()方法進行消費數據時,生產者線程可調用transfer方法將數據傳遞給消費者線程。如果當前沒有消費者線程的話,生產者線程就會將數據插入到隊尾,直到有消費者能夠進行消費才能退出;
tryTransfer(E e) tryTransfer方法如果當前有消費者線程(調用take方法或者具有超時特性的poll方法)正在消費數據的話,該方法可以將數據立即傳送給消費者線程,如果當前沒有消費者線程消費數據的話,就立即返回false。因此,與transfer方法相比,transfer方法是必須等到有消費者線程消費數據時,生產者線程才能夠返回。而tryTransfer方法能夠立即返回結果退出。
tryTransfer(E e,long timeout,imeUnit unit)</br> 與transfer基本功能一樣,只是增加了超時特性,如果數據在規定的超時時間內沒有消費者進行消費的話,就返回false。
6.LinkedBlockingDeque
LinkedBlockingDeque是基于鏈表數據結構的有界阻塞雙端隊列,如果在創建對象時為指定大小時,其默認大小為Integer.MAX_VALUE。與LinkedBlockingQueue相比,主要的不同點在于,LinkedBlockingDeque具有雙端隊列的特性。LinkedBlockingDeque基本操作如下圖所示(來源于java文檔)
?
如上圖所示,LinkedBlockingDeque的基本操作可以分為四種類型:1.特殊情況,拋出異常;2.特殊情況,返回特殊值如null或者false;3.當線程不滿足操作條件時,線程會被阻塞直至條件滿足;4. 操作具有超時特性。
另外,LinkedBlockingDeque實現了BlockingDueue接口而LinkedBlockingQueue實現的是BlockingQueue,這兩個接口的主要區別如下圖所示(來源于java文檔):
從上圖可以看出,兩個接口的功能是可以等價使用的,比如BlockingQueue的add方法和BlockingDeque的addLast方法的功能是一樣的。
7.DelayQueue
DelayQueue是一個存放實現Delayed接口的數據的無界阻塞隊列,只有當數據對象的延時時間達到時才能插入到隊列進行存儲。如果當前所有的數據都還沒有達到創建時所指定的延時期,則隊列沒有隊頭,并且線程通過poll等方法獲取數據元素則返回null。所謂數據延時期滿時,則是通過Delayed接口的getDelay(TimeUnit.NANOSECONDS)來進行判定,如果該方法返回的是小于等于0則說明該數據元素的延時期已滿。
應用場景
(1)數據緩存
轉載于:https://www.cnblogs.com/itxiaok/p/10356592.html
總結
以上是生活随笔為你收集整理的19.并发容器之BlockingQueue的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: t2 初识Tornado
- 下一篇: PAT A1084