blockingQueue(阻塞隊列)在java併發庫包中,很好的解決了消費者和生產者結構的模式的問題(隊列)。
假設我們有若干生產者線程,另外又有若干個消費者線程。如果生產者線程需要把準備好的數據共享給消費者線程,利用隊列的方式來傳遞數據,就可以很方便地解決他們之間的數據共享問題。當隊列中填滿數據的情況下,生產者端的所有線程都會被自動阻塞,直到隊列中有空的位置,線程被自動喚醒。我們不用關心什麼時候需要阻塞線程,什麼時候喚醒線程,這一切都由blockingQueue自動完成。
隊列模式主要有兩種:
先進先出(FIFO):先插入的隊列的元素也最先出隊列,類似於排隊的功能。從某種程度上來說這種隊列也體現了一種公平性。
後進先出(LIFO):後插入隊列的元素最先出隊列,這種隊列優先處理最近發生的事件。
BlockingQueue的具體實現有以下幾類:
ArrayBlockingQueue:
//數組阻塞隊列,內部定義了一個緩存數組,還有兩個變量,分別標識隊列頭部和尾部的位置
LinkedBlockingQueue:
//基於鏈表的阻塞隊列,內部定義了一個鏈表式的緩存隊列。
//我們需要注意的是,如果構造一個LinkedBlockingQueue對象,而沒有指定其容量大小,LinkedBlockingQueue會默認一個類似無限大小的容量(Integer.MAX_VALUE),
//這樣的話,如果生產者的速度一旦大於消費者的速度,也許還沒有等到隊列滿阻塞產生,系統內存就有可能已被消耗殆盡了。
DelayQueue:
//DelayQueue中的元素只有當其指定的延遲時間到了,才能夠從隊列中獲取到該元素。DelayQueue是一個沒有大小限制的隊列,因此往隊列中插入數據的操作(生產者)
//永遠不會被阻塞,而只有獲取數據的操作(消費者)纔會被阻塞。
PriorityBlockingQueue:
//基於優先級的阻塞隊列(優先級的判斷通過構造函數傳入的Compator對象來決定),但需要注意的是PriorityBlockingQueue並不會阻塞數據生產者,
//而只會在沒有可消費的數據時,阻塞數據的消費者。因此使用的時候要特別注意,生產者生產數據的速度絕對不能快於消費者消費數據的速度,
//否則時間一長,會最終耗盡所有的可用堆內存空間。
SynchronousQueue:
一種無緩衝的等待隊列,消費者和生產者直接交易。
這五種具體實現,經常使用的只有ArrayBlockingQueue和LinkedBlockingQueue。
BlockingQueue的方法主要有:
放入數據:
offer(anObject):
//表示如果可能的話,將anObject加到BlockingQueue裏,即如果BlockingQueue可以容納,則返回true,否則返回false.(本方法不阻塞當前執行方法的線程)
offer(E o, long timeout, TimeUnit unit):
//可以設定等待的時間,如果在指定的時間內,還不能往隊列中加入BlockingQueue,則返回失敗。
put(anObject):
//把anObject加到BlockingQueue裏,如果BlockQueue沒有空間,則調用此方法的線程被阻斷直到BlockingQueue裏面有空間再繼續.
獲取數據:poll(time):
//取走BlockingQueue裏排在首位的對象,若不能立即取出,則可以等time參數規定的時間,取不到時返回null;
poll(long timeout, TimeUnit unit):
//從BlockingQueue取出一個隊首的對象,如果在指定時間內,隊列一旦有數據可取,則立即返回隊列中的數據。否則知道時間超時還沒有數據可取,返回失敗。
take():
//取走BlockingQueue裏排在首位的對象,若BlockingQueue爲空,阻斷進入等待狀態直到BlockingQueue有新的數據被加入;
drainTo():
//一次性從BlockingQueue獲取所有可用的數據對象(還可以指定獲取數據的個數),通過該方法,可以提升獲取數據效率;不需要多次分批加鎖或釋放鎖。
ArrayBlockingQueue:
public class BlockingQueueTest {
public static void main(String[] args) {
final BlockingQueue queue = new ArrayBlockingQueue(3);
for(int i=0;i<2;i++){
new Thread(){
public void run(){
while(true){
try {
Thread.sleep((long)(Math.random()*1000));
System.out.println(Thread.currentThread().getName() + "準備放數據!");
queue.put(1);
System.out.println(Thread.currentThread().getName() + "已經放了數據," +
"隊列目前有" + queue.size() + "個數據");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}.start();
}
new Thread(){
public void run(){
while(true){
try {
//將此處的睡眠時間分別改爲100和1000,觀察運行結果
Thread.sleep(1000);
System.out.println(Thread.currentThread().getName() + "準備取數據!");
queue.take();
System.out.println(Thread.currentThread().getName() + "已經取走數據," +
"隊列目前有" + queue.size() + "個數據");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}.start();
}
}
打印結果:
Thread-0準備放數據!
Thread-0已經放了數據,隊列目前有1個數據
Thread-1準備放數據!
Thread-1已經放了數據,隊列目前有2個數據
Thread-0準備放數據!
Thread-0已經放了數據,隊列目前有3個數據
Thread-2準備取數據!
Thread-2已經取走數據,隊列目前有2個數據
Thread-1準備放數據!
Thread-1已經放了數據,隊列目前有3個數據
Thread-1準備放數據!
Thread-0準備放數據!
Thread-2準備取數據!
Thread-2已經取走數據,隊列目前有2個數據
Thread-1已經放了數據,隊列目前有3個數據
LinkBlockingQueue:
public class BlockingQueueTest2 {
/**
*
* 定義裝蘋果的籃子
*
*/
public class Basket {
// 籃子,能夠容納3個蘋果
BlockingQueue<String> basket = new LinkedBlockingQueue<String>(3);
// 生產蘋果,放入籃子
public void produce() throws InterruptedException {
// put方法放入一個蘋果,若basket滿了,等到basket有位置
basket.put("An apple");
}
// 消費蘋果,從籃子中取走
public String consume() throws InterruptedException {
// take方法取出一個蘋果,若basket爲空,等到basket有蘋果爲止(獲取並移除此隊列的頭部)
return basket.take();
}
}
// 定義蘋果生產者
class Producer implements Runnable {
private String instance;
private Basket basket;
public Producer(String instance, Basket basket) {
this.instance = instance;
this.basket = basket;
}
public void run() {
try {
while (true) {
// 生產蘋果
System.out.println("生產者準備生產蘋果:" + instance);
basket.produce();
System.out.println("!生產者生產蘋果完畢:" + instance);
// 休眠300ms
Thread.sleep(300);
}
} catch (InterruptedException ex) {
System.out.println("Producer Interrupted");
}
}
}
// 定義蘋果消費者
class Consumer implements Runnable {
private String instance;
private Basket basket;
public Consumer(String instance, Basket basket) {
this.instance = instance;
this.basket = basket;
}
public void run() {
try {
while (true) {
// 消費蘋果
System.out.println("消費者準備消費蘋果:" + instance);
System.out.println(basket.consume());
System.out.println("!消費者消費蘋果完畢:" + instance);
// 休眠1000ms
Thread.sleep(1000);
}
} catch (InterruptedException ex) {
System.out.println("Consumer Interrupted");
}
}
}
public static void main(String[] args) {
BlockingQueueTest2 test = new BlockingQueueTest2();
// 建立一個裝蘋果的籃子
Basket basket = test.new Basket();
ExecutorService service = Executors.newCachedThreadPool();
Producer producer = test.new Producer("生產者001", basket);
Producer producer2 = test.new Producer("生產者002", basket);
Consumer consumer = test.new Consumer("消費者001", basket);
service.submit(producer);
service.submit(producer2);
service.submit(consumer);
// 程序運行5s後,所有任務停止
// try {
// Thread.sleep(1000 * 5);
// } catch (InterruptedException e) {
// e.printStackTrace();
// }
// service.shutdownNow();
}
}