[Java併發程式設計實戰] 阻塞佇列 BlockingQueue(含程式碼,生產者-消費者模型)
見賢思齊焉,見不賢而內自省也。—《論語》
Java5.0 增加了兩種新的容器型別,它們是指:Queue 和 BlockingQueue。Queue 用來臨時儲存一組等待處理的元素。BlockingQueue 擴張了 Queue 介面,增加了可阻塞的插入和獲取等操作。
BlockingQueue 通常運用於一個執行緒生產物件放入佇列,另一個執行緒從佇列獲取物件並消費,這是典型的生產者消費者模型。
生產者執行緒持續生產新物件並插入佇列,如果佇列已滿,那麼插入物件的操作會一直阻塞,直到佇列中出現可用的空間。
消費者執行緒持續從佇列獲取物件,如果佇列為空,那麼獲取操作會一直阻塞,直到佇列中出現可用的新物件。BlockingQueue 簡化了生產者-消費者設計的實現過程,它支援任意數量的生產者和消費者。
BlockingQueue 的核心方法:
offer(E): 向佇列插入元素,並返回插入成功與否。本方法不阻塞當前執行執行緒。
put(E) : 向佇列插入元素,如果佇列已滿,則會阻塞當前執行緒直至元素加入佇列。
take() : 獲取佇列的首位元素,如果佇列為空,則阻塞當前執行緒直至佇列有元素並取走。
poll():獲取佇列首個元素,指定時間內一旦資料可取,則立即返回;否則返回失敗。
remove(E):刪除佇列中的元素,返回成功與否。
BlockingQueue 的實現
BlockingQueue是一個介面,所以你必須使用它的實現來使用它。它的實現包括以下幾個:
ArrayBlockingQueue:基於陣列實現的有界佇列(FIFO),使用一把全域性鎖並行對 queue 讀寫操作,同時使用兩個 Condition 阻塞佇列為空時的取操作和佇列為滿時的寫操作。
LinkedBlockingQueue:基於已連結節點的,範圍上限為 Integer.MAX_VALUE 的 blocking queue(FIFO)。主要操作 put 和 take 都是阻塞的。
DelayQueue:當指定的延遲時間到了,才能夠從佇列中獲取元素。它沒有大小限制,因此插入元素時不會阻塞,而只有獲取元素時才會被阻塞。它的用法可以參考下面兩篇部落格:
http://www.cnblogs.com/jobs/archive/2007/04/27/730255.html,
http://www.cnblogs.com/sunzhenchao/p/3515085.htmlPriorityBlockingQueue: 基於優先順序的阻塞佇列,但需要注意的是PriorityBlockingQueue並不會阻塞資料生產者,而只會在沒有可消費的資料時,阻塞資料的消費者。
SynchronoutQueue:它的內部同時只能夠容納單個元素。如果該佇列已有一個元素的話,試圖向佇列插入一個新元素執行緒會阻塞,知道另一個執行緒將該元素從佇列中拿走。同樣,如果該佇列為空,試圖向佇列中抽取一個元素的執行緒將會阻塞,知道另一個執行緒向佇列中插入一個新的元素。SynchronousQueue適合一對一的匹配場景,沒有容量,無法快取。它的用法強烈推薦部落格:
http://www.cnblogs.com/leesf456/p/5560362.html
BlockingQueue的使用
這是一個使用 BlockingQueue 的例子,本例使用 ArrayBlockingQueue 實現。首先,BlockingQueueTest 建立一個生產者執行緒 Procucer, 把字元存放進共享佇列。然後建立三個消費者執行緒 Consumer,把字串從佇列中取出。Consumer 取到最後一個字串時,中斷所有消費者執行緒,結束程式。
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Vector;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue;
public class BlockingQueueTest {
//佇列容量
private static final int SIZE = 5;
private static final int CONSUMER_SIZE = 3;
//消費者執行緒退出標誌
private static String endString = "num:" + (SIZE*2-1);
//存放消費者執行緒
private static List list = new ArrayList<Thread>();
public static void main(String[] args) throws Exception{
//建立固定長度的阻塞佇列
BlockingQueue q = new ArrayBlockingQueue<String>(SIZE);
//建立生產者
Producer producer = new Producer(q);
//啟動生產者執行緒,生產物件
producer.start();
//啟動消費者執行緒,獲取佇列物件
for(int i = 0; i < CONSUMER_SIZE; i++) {
list.add(new Consumer(q));
((Thread) list.get(i)).start();
}
}
//中斷執行緒
public static void shutDownThread() {
for(int i = 0; i < CONSUMER_SIZE; i++) {
((Thread) list.get(i)).interrupt();
}
}
static class Producer extends Thread{
private BlockingQueue queue = null;
public Producer(BlockingQueue q) {
this.queue = q;
}
@Override
public void run() {
// TODO Auto-generated method stub
try {
//生產10個物件,放進佇列
for(int i = 0; i < SIZE*2; i++) {
String str = "num:" + i;
System.out.println(Thread.currentThread().getName() +":"+"IN: " + str);
queue.put(str);
Thread.sleep(100);
}
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
//消費者執行緒
static class Consumer extends Thread{
private BlockingQueue queue = null;
public Consumer(BlockingQueue q) {
this.queue = q;
}
@Override
public void run() {
// TODO Auto-generated method stub
try {
//獲取佇列的元素,佇列為空時會阻塞
while(true) {
String str = (String) queue.take();
System.out.println(Thread.currentThread().getName() + ":" + "OUT " + str);
//已經取出最後一個元素,消費者執行緒應該退出,否則程式一直在執行
if(str.equals(endString)) {
shutDownThread();//中斷執行緒
}
}
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
執行結果:
總結
java.util.concurrent 中實現的阻塞佇列包含了足夠的同步機制,從而能夠安全的將物件從生產者執行緒釋出到消費者執行緒。對於可變物件,生產者-消費者模型,把物件的所有權安全的從生產者交付給消費者。轉移後,消費者執行緒獲得這個物件的所有權(獨佔訪問權,可以任意修改它),並且生產者執行緒不會再訪問它。同時,阻塞佇列負責所有的控制流,使得消費者生產者的程式碼更加簡單和清晰。
本文完畢,如對你有幫助,請關注我,謝謝~
參考
相關文章
- 什麼是阻塞佇列?如何使用阻塞佇列來實現生產者-消費者模型?佇列模型
- 阻塞佇列和生產者-消費者模式佇列模式
- golang 併發程式設計之生產者消費者Golang程式設計
- Java併發程式設計:阻塞佇列Java程式設計佇列
- Java併發程式設計——阻塞佇列Java程式設計佇列
- 併發設計模式---生產者/消費者模式設計模式
- rabbitMQ實戰生產者-交換機-佇列-消費者細談MQ佇列
- Java實現生產者-消費者模型Java模型
- Java多執行緒15:Queue、BlockingQueue以及利用BlockingQueue實現生產者/消費者模型Java執行緒BloC模型
- Java併發系列 — 阻塞佇列(BlockingQueue)Java佇列BloC
- Python並行程式設計(六):多執行緒同步之queue(佇列)實現生產者-消費者模型Python並行行程程式設計執行緒佇列模型
- 用阻塞佇列實現一個生產者消費者模型?synchronized和lock有什麼區別?佇列模型synchronized
- Java多執行緒-併發協作(生產者消費者模型)Java執行緒模型
- 生產者消費者模型模型
- 記錄一次日常開發中基於阻塞佇列的生產者和消費者模型佇列模型
- java併發程式設計工具類JUC第一篇:BlockingQueue阻塞佇列Java程式設計BloC佇列
- 架構設計:生產者/消費者模式[2]:佇列緩衝區架構模式佇列
- 2.Python程式間的通訊之佇列(Queue)和生產者消費者模型Python佇列模型
- PHP操作Beanstalkd佇列(2)生產者與消費者PHPBean佇列
- 實戰Spring4+ActiveMQ整合實現訊息佇列(生產者+消費者)SpringMQ佇列
- java編寫生產者/消費者模式的程式。Java模式
- 生產者消費者問題-C++程式碼實現C++
- Java多執行緒之併發協作生產者消費者設計模式Java執行緒設計模式
- java 執行緒池、多執行緒併發實戰(生產者消費者模型 1 vs 10) 附案例原始碼Java執行緒模型原始碼
- Java實現生產者和消費者Java
- Java併發指南11:解讀 Java 阻塞佇列 BlockingQueueJava佇列BloC
- 生產者消費者模式,以及基於BlockingQueue的快速實現模式BloC
- JAVA執行緒消費者與生產者模型Java執行緒模型
- Python並行程式設計(九):多程式物件交換之pipe(管道)實現生產者-消費者模型Python並行行程程式設計物件模型
- 使用Disruptor實現生產者和消費者模型模型
- java實現生產者消費者問題Java
- 【RabbitMQ】生產者,消費者,通道,佇列,交換器和繫結MQ佇列
- Java多執行緒14:生產者/消費者模型Java執行緒模型
- 新手練習-消費者生產者模型模型
- 多執行緒下的生產者和消費者-BlockingQueue執行緒BloC
- 【java併發程式設計】Lock & Condition 協調同步生產消費Java程式設計
- 使用Python佇列和多執行緒實現生產者消費者Python佇列執行緒
- 多執行緒併發如何高效實現生產者/消費者?執行緒