blockingqueue即阻塞隊列,從阻塞這個詞可以看出,在某些情況下對阻塞隊列的通路可能會造成阻塞。被阻塞的情況主要有如下兩種:
是以,當一個線程試圖對一個已經滿了的隊列進行入隊列操作時,它将會被阻塞,除非有另一個線程做了出隊列操作;同樣,當一個線程試圖對一個空隊列進行出隊列操作時,它将會被阻塞,除非有另一個線程進行了入隊列操作。
在java中,blockingqueue的接口位于<code>java.util.concurrent</code> 包中(在java5版本開始提供),由上面介紹的阻塞隊列的特性可知,阻塞隊列是線程安全的。
阻塞隊列主要用在生産者/消費者的場景,下面這幅圖展示了一個線程生産、一個線程消費的場景:
負責生産的線程不斷的制造新對象并插入到阻塞隊列中,直到達到這個隊列的上限值。隊列達到上限值之後生産線程将會被阻塞,直到消費的線程對這個隊列進行消費。同理,負責消費的線程不斷的從隊列中消費對象,直到這個隊列為空,當隊列為空時,消費線程将會被阻塞,除非隊列中有新的對象被插入。
阻塞隊列一共有四套方法分别用來進行<code>insert</code>、<code>remove</code>和<code>examine</code>,當每套方法對應的操作不能馬上執行時會有不同的反應,下面這個表格就分類列出了這些方法:
asdf
-
throws exception
special value
blocks
times out
insert
add(o)
offer(o)
put(o)
offer(o, timeout, timeunit)
remove
remove(o)
poll()
take()
poll(timeout, timeunit)
examine
element()
peek()
這四套方法對應的特點分别是:
需要注意的是,我們不能向blockingqueue中插入<code>null</code>,否則會報<code>nullpointerexception</code>。
blockingqueue隻是<code>java.util.concurrent</code>包中的一個接口,而在具體使用時,我們用到的是它的實作類,當然這些實作類也位于<code>java.util.concurrent</code>包中。在java6中,blockingqueue的實作類主要有以下幾種:
下面我們就分别介紹這幾個實作類。
arrayblockingqueue是一個有邊界的阻塞隊列,它的内部實作是一個數組。有邊界的意思是它的容量是有限的,我們必須在其初始化的時候指定它的容量大小,容量大小一旦指定就不可改變。
arrayblockingqueue是以先進先出的方式存儲資料,最新插入的對象是尾部,最新移出的對象是頭部。下面是一個初始化和使用arrayblockingqueue的例子:
delayqueue阻塞的是其内部元素,delayqueue中的元素必須實作 <code>java.util.concurrent.delayed</code>接口,這個接口的定義非常簡單:
<code>getdelay()</code>方法的傳回值就是隊列元素被釋放前的保持時間,如果傳回<code>0</code>或者一個<code>負值</code>,就意味着該元素已經到期需要被釋放,此時delayedqueue會通過其<code>take()</code>方法釋放此對象。
從上面delayed 接口定義可以看到,它還繼承了<code>comparable</code>接口,這是因為delayedqueue中的元素需要進行排序,一般情況,我們都是按元素過期時間的優先級進行排序。
首先,我們先定義一個元素,這個元素要實作delayed接口
設定這個元素的過期時間為3s
}
運作這個main函數,我們可以發現,我們需要等待3s之後才會列印這個對象。
其實delayqueue應用場景很多,比如定時關閉連接配接、緩存對象,逾時處理等各種場景,下面我們就拿學生考試為例讓大家更深入的了解delayqueue的使用。
首先,我們構造一個學生對象
然後在構造一個教師對象對學生進行考試
我們看一下運作結果:
通過運作結果我們可以發現,每個學生在指定開始時間到達之後就會“交卷”(取決于getdelay()方法),并且是先做完的先交卷(取決于compareto()方法)。
通過檢視其源碼可以看到,delayqueue内部實作用的是priorityqueue和一個lock:
linkedblockingqueue阻塞隊列大小的配置是可選的,如果我們初始化時指定一個大小,它就是有邊界的,如果不指定,它就是無邊界的。說是無邊界,其實是采用了預設大小為<code>integer.max_value</code>的容量 。它的内部實作是一個連結清單。
和arrayblockingqueue一樣,linkedblockingqueue 也是以先進先出的方式存儲資料,最新插入的對象是尾部,最新移出的對象是頭部。下面是一個初始化和使linkedblockingqueue的例子:
priorityblockingqueue是一個沒有邊界的隊列,它的排序規則和 <code>java.util.priorityqueue</code>一樣。需要注意,priorityblockingqueue中允許插入null對象。
所有插入priorityblockingqueue的對象必須實作 <code>java.lang.comparable</code>接口,隊列優先級的排序規則就是按照我們對這個接口的實作來定義的。
另外,我們可以從priorityblockingqueue獲得一個疊代器iterator,但這個疊代器并不保證按照優先級順序進行疊代。
下面我們舉個例子來說明一下,首先我們定義一個對象類型,這個對象需要實作comparable接口:
然後我們把這些元素随機設定優先級放入隊列中
看一下運作結果:
synchronousqueue隊列内部僅允許容納一個元素。當一個線程插入一個元素後會被阻塞,除非這個元素被另一個線程消費。