1. 程式人生 > >java cocurrent並發包

java cocurrent並發包

解釋 之一 取出 想要 表示 並發 方式 rem 自己


1. java.util.concurrent - Java 並發工具包
Java 5 添加了一個新的包到 Java 平臺,java.util.concurrent 包。這個包包含有一系列能夠讓 Java 的並發編程變得更加簡單輕松的類。在這個包被添加以前,你需要自己去動手實現自己的相關工具類。
本文我將帶你一一認識 java.util.concurrent 包裏的這些類,然後你可以嘗試著如何在項目中使用它們。本文中我將使用 Java 6 版本,我不確定這和 Java 5 版本裏的是否有一些差異。
我不會去解釋關於 Java 並發的核心問題 - 其背後的原理,也就是說,如果你對那些東西感興趣,參考《Java 並發指南》。

半成品
本文很大程度上還是個 "半成品",所以當你發現一些被漏掉的類或接口時,請耐心等待。在我空閑的時候會把它們加進來的。

2. 阻塞隊列 BlockingQueue
java.util.concurrent 包裏的 BlockingQueue 接口表示一個線程安放入和提取實例的隊列。本小節我將給你演示如何使用這個 BlockingQueue。
本節不會討論如何在 Java 中實現一個你自己的 BlockingQueue。如果你對那個感興趣,參考《Java 並發指南》
BlockingQueue 用法
BlockingQueue 通常用於一個線程生產對象,而另外一個線程消費這些對象的場景。下圖是對這個原理的闡述:


一個線程往裏邊放,另外一個線程從裏邊取的一個 BlockingQueue。
一個線程將會持續生產新對象並將其插入到隊列之中,直到隊列達到它所能容納的臨界點。也就是說,它是有限的。如果該阻塞隊列到達了其臨界點,負責生產的線程將會在往裏邊插入新對象時發生阻塞。它會一直處於阻塞之中,直到負責消費的線程從隊列中拿走一個對象。
負責消費的線程將會一直從該阻塞隊列中拿出對象。如果消費線程嘗試去從一個空的隊列中提取對象的話,這個消費線程將會處於阻塞之中,直到一個生產線程把一個對象丟進隊列。
BlockingQueue 的方法
BlockingQueue 具有 4 組不同的方法用於插入、移除以及對隊列中的元素進行檢查。如果請求的操作不能得到立即執行的話,每個方法的表現也不同。這些方法如下:

拋異常 特定值 阻塞 超時
插入 add(o) offer(o) put(o) offer(o, timeout, timeunit)
移除 remove(o) poll(o) take(o) poll(timeout, timeunit)
檢查 element(o) peek(o)

四組不同的行為方式解釋:
拋異常:如果試圖的操作無法立即執行,拋一個異常。
特定值:如果試圖的操作無法立即執行,返回一個特定的值(常常是 true / false)。
阻塞:如果試圖的操作無法立即執行,該方法調用將會發生阻塞,直到能夠執行。
超時:如果試圖的操作無法立即執行,該方法調用將會發生阻塞,直到能夠執行,但等待時間不會超過給定值。返回一個特定值以告知該操作是否成功(典型的是 true / false)。
無法向一個 BlockingQueue 中插入 null。如果你試圖插入 null,BlockingQueue 將會拋出一個 NullPointerException。
可以訪問到 BlockingQueue 中的所有元素,而不僅僅是開始和結束的元素。比如說,你將一個對象放入隊列之中以等待處理,但你的應用想要將其取消掉。那麽你可以調用諸如 remove(o) 方法來將隊列之中的特定對象進行移除。但是這麽幹效率並不高(譯者註:基於隊列的數據結構,獲取除開始或結束位置的其他對象的效率不會太高),因此你盡量不要用這一類的方法,除非你確實不得不那麽做。
BlockingQueue 的實現
BlockingQueue 是個接口,你需要使用它的實現之一來使用 BlockingQueue。java.util.concurrent 具有以下 BlockingQueue 接口的實現(Java 6):
ArrayBlockingQueue
DelayQueue
LinkedBlockingQueue
PriorityBlockingQueue
SynchronousQueue
Java 中使用 BlockingQueue 的例子
這裏是一個 Java 中使用 BlockingQueue 的示例。本示例使用的是 BlockingQueue 接口的 ArrayBlockingQueue 實現。
首先,BlockingQueueExample 類分別在兩個獨立的線程中啟動了一個 Producer 和 一個 Consumer。Producer 向一個共享的 BlockingQueue 中註入字符串,而 Consumer 則會從中把它們拿出來。
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);
}
}

以下是 Producer 類。註意它在每次 put() 調用時是如何休眠一秒鐘的。這將導致 Consumer 在等待隊列中對象的時候發生阻塞。
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();
}
}
}

以下是 Consumer 類。它只是把對象從隊列中抽取出來,然後將它們打印到 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();
}
}
}

3. 數組阻塞隊列 ArrayBlockingQueue
ArrayBlockingQueue 類實現了 BlockingQueue 接口。
ArrayBlockingQueue 是一個有界的阻塞隊列,其內部實現是將對象放到一個數組裏。有界也就意味著,它不能夠存儲無限多數量的元素。它有一個同一時間能夠存儲元素數量的上限。你可以在對其初始化的時候設定這個上限,但之後就無法對這個上限進行修改了(譯者註:因為它是基於數組實現的,也就具有數組的特性:一旦初始化,大小就無法修改)。
ArrayBlockingQueue 內部以 FIFO(先進先出)的順序對元素進行存儲。隊列中的頭元素在所有元素之中是放入時間最久的那個,而尾元素則是最短的那個。
以下是在使用 ArrayBlockingQueue 的時候對其初始化的一個示例:
BlockingQueue queue = new ArrayBlockingQueue(1024);

queue.put("1");

Object object = queue.take();

以下是使用了 Java 泛型的一個 BlockingQueue 示例。註意其中是如何對 String 元素放入和提取的:
BlockingQueue<String> queue = new ArrayBlockingQueue<String>(1024);

queue.put("1");

String string = queue.take();

java cocurrent並發包