天天看點

【Java并發之】BlockingQueue

blockingqueue即阻塞隊列,從阻塞這個詞可以看出,在某些情況下對阻塞隊列的通路可能會造成阻塞。被阻塞的情況主要有如下兩種:

是以,當一個線程試圖對一個已經滿了的隊列進行入隊列操作時,它将會被阻塞,除非有另一個線程做了出隊列操作;同樣,當一個線程試圖對一個空隊列進行出隊列操作時,它将會被阻塞,除非有另一個線程進行了入隊列操作。

在java中,blockingqueue的接口位于<code>java.util.concurrent</code> 包中(在java5版本開始提供),由上面介紹的阻塞隊列的特性可知,阻塞隊列是線程安全的。

阻塞隊列主要用在生産者/消費者的場景,下面這幅圖展示了一個線程生産、一個線程消費的場景:

【Java并發之】BlockingQueue

負責生産的線程不斷的制造新對象并插入到阻塞隊列中,直到達到這個隊列的上限值。隊列達到上限值之後生産線程将會被阻塞,直到消費的線程對這個隊列進行消費。同理,負責消費的線程不斷的從隊列中消費對象,直到這個隊列為空,當隊列為空時,消費線程将會被阻塞,除非隊列中有新的對象被插入。

阻塞隊列一共有四套方法分别用來進行<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:

【Java并發之】BlockingQueue

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隊列内部僅允許容納一個元素。當一個線程插入一個元素後會被阻塞,除非這個元素被另一個線程消費。

上一篇: Object類型