在實際編程中,會常用到JDK中Collection集合框架中的各類容器類如實現List,Map,Queue接口的容器類,可是這些容器類基本上不是線程安全的,除了使用Collections能夠將其轉換爲線程安全的容器,Doug Lea大師爲咱們都準備了對應的線程安全的容器,如實現List接口的CopyOnWriteArrayList(關於CopyOnWriteArrayList能夠看這篇文章),實現Map接口的ConcurrentHashMap(關於ConcurrentHashMap能夠看這篇文章),實現Queue接口的ConcurrentLinkedQueue(關於ConcurrentLinkedQueue能夠看這篇文章)。java
最經常使用的"生產者-消費者"問題中,隊列一般被視做線程間操做的數據容器,這樣,能夠對各個模塊的業務功能進行解耦,生產者將「生產」出來的數據放置在數據容器中,而消費者僅僅只須要在「數據容器」中進行獲取數據便可,這樣生產者線程和消費者線程就可以進行解耦,只專一於本身的業務功能便可。阻塞隊列(BlockingQueue)被普遍使用在「生產者-消費者」問題中,其緣由是BlockingQueue提供了可阻塞的插入和移除的方法。當隊列容器已滿,生產者線程會被阻塞,直到隊列未滿;當隊列容器爲空時,消費者線程會被阻塞,直至隊列非空時爲止。編程
BlockingQueue基本操做總結以下(此圖來源於JAVA API文檔):數組
BlockingQueue繼承於Queue接口,所以,對數據元素的基本操做有:安全
插入元素數據結構
true
,不然則返回false
。當隊列滿時不會拋出異常;刪除元素框架
true
,不然爲false
查看元素post
BlockingQueue具備的特殊操做:spa
插入數據:線程
刪除數據code
實現BlockingQueue接口的有ArrayBlockingQueue, DelayQueue, LinkedBlockingDeque, LinkedBlockingQueue, LinkedTransferQueue, PriorityBlockingQueue, SynchronousQueue
,而這幾種常見的阻塞隊列也是在實際編程中會經常使用的,下面對這幾種常見的阻塞隊列進行說明:
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)
與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則說明該數據元素的延時期已滿。