從J2SE 5.0開始有了java.util.concurrent套件,其中的類可以使實現多線程相關功能更為方便。本節將簡介concurrent套件中的幾個簡單常用的類。
15.3.1 BlockingQueue
隊列(Queue)是一個先進先出(First In First Out, FIFO)的數據結構。在J2SE 5.0中增加了java.util.concurrent.BlockingQueue。在多線程情況下,如果BlockingQueue的內容為空,而有個線程試圖從Queue中取出元素,則該線程會被Block,直到Queue有元素時才解除Block;反過來,如果 BlockingQueue滿了,而有個線程試圖再把數據填入Queue中,則該線程會被Block,直到Queue中有元素被取走后解除Block。
BlockingQueue的幾個主要操作如表15-1所示。
表15-1 BlockingQueue的幾個操作
![]() |
java.util.concurrent中提供幾種不同的BlockingQueue。ArrayBlockingQueue要指定容量大小來構建。LinkedBlockingQueue默認沒有容量上限,但也可以指定容量上限。PriorityBlockingQueue嚴格來說不是Queue,因為它是根據優先權(Priority)來移除元素。
我們以在wait()、notify()介紹時的生產者、消費者程序為例,使用BlockQueue來加以改寫,優點是不用親自處理wait()、notify()的細節。首先生產者改寫如范例15.21所示:
范例15.21 ProducerQueue.java
package onlyfun.caterpillar; import java.util.concurrent.BlockingQueue; public class ProducerQueue implements Runnable { private BlockingQueue<Integer> queue; public ProducerQueue(BlockingQueue<Integer> queue) { this.queue = queue; } public void run() { for(int product = 1; product <= 10; product++) { try { // wait for a random time Thread.sleep((int) Math.random() * 3000); queue.put(product); } catch(InterruptedException e) { e.printStackTrace(); } } } } |
可以看到,直接使用BlockingQueue,會自動處理同步化、wait()和notify()的執行。消費者類改寫如范例15.22所示:
范例15.22 ConsumerQueue.java
package onlyfun.caterpillar; import java.util.concurrent.BlockingQueue; public class ConsumerQueue implements Runnable { private BlockingQueue<Integer> queue; public ConsumerQueue(BlockingQueue<Integer> queue) { this.queue = queue; } public void run() { for(int i = 1; i <= 10; i++) { try { // wait for a random time Thread.sleep((int) (Math.random() * 3000)); queue.take(); } catch(InterruptedException e) { e.printStackTrace(); } } } } |
可以使用范例15.23進行簡單的測試:
范例15.23 BlockingQueueDemo.java
package onlyfun.caterpillar; import java.util.concurrent.BlockingQueue; public class ConsumerQueue implements Runnable { private BlockingQueue<Integer> queue; public ConsumerQueue(BlockingQueue<Integer> queue) { this.queue = queue; } public void run() { for(int i = 1; i <= 10; i++) { try { // 等待一個隨機時間 Thread.sleep((int) (Math.random() * 3000)); queue.take(); } catch(InterruptedException e) { e.printStackTrace(); } } } } |
由于BlockingQueue不需要您來控制,所以沒有特意顯示信息以表示生產者、消費者放入產品至Queue的信息,不過仍可以在ProducerQueue與ConsumerQueue中放入相關信息顯示,以確認程序確實在運轉。