java.util.concurrent包中的BlockingQueue介面是一個能夠安全執行緒的插入和取出對象執行個體的阻塞隊列。這篇文章將展示如何使用BlockingQueue。
本文不會討論如何利用Java去實現一個BlockingQueue。如果你有興趣想去自己實現,你可以參考Java Concurrency Tutorial裡面關於Blocking Queue部分的內容。
BlockingQueue Usage
BlockingQueue通常用於一個線程生產對象而另外一個線程消費這些對象的情景。下圖是對這個原理的闡述:
![BlockingQueue原理][http://tutorials.jenkov.com/images/java-concurrency-utils/blocking-queue.png]
生產線程會持續產生新對象並將其插入到阻塞隊列之中,直到隊列達到它儲存容量的臨界點。也就是說,阻塞隊列的容量是有限的。如果阻塞隊列到達了儲存的臨界點,當生產線程嘗試插入新的對象時將會一直阻塞,直到消費線程從阻塞隊列中取出一個對象。
消費線程則會持續的從阻塞隊列取出對象並進行處理。當消費線程試著從一個空的隊列取一個對象時,它會一直阻塞直到生產線程將一個對象放入隊列中。
BlockingQueue Methods
BlockingQueue有4組不同的方法用於插入,刪除和檢查隊列中的對象。如果請求的方法不能夠立即執行時,每組方法的結果也是不同的。下表展示了這些方法的返回結果。
| |
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() |
|
|
4組不同行為方法的解釋如下:
1. Throw Exception: 如果試圖調用的操作無法立即執行,則拋出一個異常。
2. Special Value: 如果試圖調用的操作無法立即執行,則返回一個特殊值(通常是true/false)。
3. Blocks: 如果試圖調用的操作無法立即執行,該方法調用會一直阻塞直到可以立即執行為止。
4. Times Out: 如果試圖調用的操作無法立即執行,該方法調用會一直阻塞直到可以立即執行為止,但是等待時間不會超過給定的時間間隔(timeout)。返回一個特定值來表示該方法是否調用成功(通常是true/false)。
BlockingQueue中不能插入null。如果你試圖插入一個null, BlockingQueue將會拋出NullPointerException異常。
BlcokingQueue內部所有元素都可以被訪問,而不僅僅是頭部和尾部的元素。例如,你將一個對象放入隊列中等待處理,但你的應用想取消這個任務。這時,你就可以調用諸如remove(o)方法來刪除隊列中的特定元素。但是這樣的操作效率不會很高,因此,除非你迫不得已,否則盡量不要使用這類方法。
BlockingQueue Implementations
BlockingQueue是個介面,你需要使用它的實作類別來使用它。java.util.concurrent包中BlockingQueue介面的實作類別如下(原文中為Java 6,譯者基於Java 8展示):
ArrayBlockingQueue
DelayQueue
LinkedBlcokingQueue
PriorityBlockingQueue
SynchronousQueue
Java BlockingQueue Example
下面展示了一個BlockingQueue的例子。例子中使用了BlockingQueue介面的實作類別之一–ArrayBlockingQueue。
首先,BlockingQueueExample類在不同的線程中分別啟動了一個生產者和一個消費者。生產者向共用的BlockingQueue中插入字串,消費者從共用的BlockingQueue中取出字串。
public class BlockingQueueExample { public static void main(String[] args) throws Exception { BlockingQueue queue = new ArrayBlockingQueue(1024); Producer producer = new Producer(queue); Consumer consumer = new Consumer(queue); new Thread(producer).start(); new Thread(consumer).start(); Thread.sleep(4000); }}
下面是生產者類。注意它在每次put()調用時是如何休眠一秒鐘的。這講使得消費者在等待取出隊列中對象時發生阻塞。
public class Producer implements Runnable{ protected BlockingQueue queue = null; public Producer(BlockingQueue queue) { this.queue = queue; } public void run() { try { queue.put("1"); Thread.sleep(1000); queue.put("2"); Thread.sleep(1000); queue.put("3"); } catch (InterruptedException e) { e.printStackTrace(); } }}
下面是消費者類。它只是從隊列中取出對象,然後將它們列印到System.out。
public class Consumer implements Runnable{ protected BlockingQueue queue = null; public Consumer(BlockingQueue queue) { this.queue = queue; } public void run() { try { System.out.println(queue.take()); System.out.println(queue.take()); System.out.println(queue.take()); } catch (InterruptedException e) { e.printStackTrace(); } }}
原文連結