阻塞隊列是在隊列基礎上又加了兩個附加操作的隊列,分別爲阻塞插入和阻塞移除。
阻塞插入:隊列滿時,隊列會阻塞插入元素的線程,直到隊列不滿
阻塞移除:隊列空時,獲取元素的線程會等待隊列變爲非空。
一個簡單例子:
public class Thread_Bdueue {
public static void main(String args[]){
BlockingDeque blockingDeque=new LinkedBlockingDeque(20);
for (int i=0;i<30;i++){
try {
//指定元素添加到阻塞棧當中,如果沒有可用空間,則一直等待
blockingDeque.putFirst(i);
System.out.println("向阻塞棧當中添加了元素:"+i);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println("程序到此運行結束-------");
}
}
應用場景:阻塞隊列應用於生產者消費者的場景,簡而言之,阻塞隊列是生產者存放元素,消費者消費元素的容器。
方法\處理方式 | 拋出異常 | 返回特殊值 | 一直阻塞 | 超時退出 |
---|---|---|---|---|
插入方法 | add(e) | offer(e) | put(e) | offer(e,time,unit) |
移除方法 | remove() | poll() | take() | poll(time,unit) |
檢查方法 | element() | peek() | 不可用 | 不可用 |
JDK7提供了7個阻塞隊列
(1)、ArrayBlockingQueue :一個由數組結構組成的有界阻塞隊列。
此隊列按照先進先出的原色對元素進行排序,默認情況下不保證公平的訪問隊列。即如果隊列滿了,那麼被阻塞在外面的線程對隊列訪問的順序是不能保證線程公平(即先阻塞,先插入)的。
(2)、LinkedBlockingQueue :一個由鏈表結構組成的有界阻塞隊列。此隊列按照先出先進的原則對元素進行排序
(3)、PriorityBlockingQueue :一個支持優先級排序的無界阻塞隊列。
(4)、DelayQueue:一個使用優先級隊列實現的無界阻塞隊列。
(5)、SynchronousQueue:一個不存儲元素的阻塞隊列。每一個put必須等待一個take操作,否則不能繼續添加元素。並且他支持公平訪問隊列。
(6)、LinkedTransferQueue:一個由鏈表結構組成的無界阻塞隊列。
(7)、LinkedBlockingDeque:一個由鏈表結構組成的雙向阻塞隊列。優勢在於多線程入隊時,減少一半的競爭。
使用BlockingQueue解決生產者和消費者的問題:
任何有效的生產者-消費者問題解決方案都是通過控制生產者put()方法(生產資源)和消費者take()方法(消費資源)的調用來實現的,一旦你實現了對方法的阻塞控制,那麼你將解決該問題。
Java通過BlockingQueue提供了開箱即用的支持來控制這些方法的調用(一個線程創建資源,另一個消費資源)。java.util.concurrent包下的BlockingQueue接口是一個線程安全的可用於存取對象的隊列。
生產者:
public class Producer implements Runnable {
protected BlockingQueue<Object> queue;
Producer(BlockingQueue<Object> theQueue) {
this.queue = theQueue;
}
Object getResource() {
try {
Thread.sleep(100);
} catch (InterruptedException ex) {
System.out.println("生產者讀中斷");
}
return new Object();
}
@Override
public void run() {
try {
while (true) {
Object justProduced = getResource();
queue.put(justProduced);
System.out.println("生產者資源隊列大小= " + queue.size());
}
} catch (InterruptedException ex) {
System.out.println("生產者中斷");
}
}
}
消費者:
public class Consumer implements Runnable {
protected BlockingQueue<Object> queue;
Consumer(BlockingQueue<Object> theQueue) {
this.queue = theQueue;
}
void take(Object obj) {
try {
Thread.sleep(100); // simulate time passing
} catch (InterruptedException ex) {
System.out.println("消費者讀中斷");
}
System.out.println("消費對象" + obj);
}
@Override
public void run() {
try {
while (true) {
Object obj = queue.take();
System.out.println("消費者資源隊列大小 " + queue.size());
take(obj);
}
} catch (InterruptedException ex) {
System.out.println("消費者中斷");
}
}
}
執行者:
public class Main {
public static void main(String args[]) throws InterruptedException {
int numProducers = 4;
int numConsumers = 3;
BlockingQueue<Object> myQueue = new LinkedBlockingQueue<Object>(5);
for (int i = 0; i < numProducers; i++) {
new Thread(new Producer(myQueue)).start();
}
for (int i = 0; i < numConsumers; i++) {
new Thread(new Consumer(myQueue)).start();
}
Thread.sleep(1000);
System.exit(0);
}
}
運行結果如下:
生產者資源隊列大小= 1
生產者資源隊列大小= 1
消費者 資源 隊列大小 1
生產者資源隊列大小= 1
消費者 資源 隊列大小 1
消費者 資源 隊列大小 1
生產者資源隊列大小= 1
生產者資源隊列大小= 3
消費對象 java.lang.Object@1e1aa52b
生產者資源隊列大小= 2
生產者資源隊列大小= 5
消費對象 java.lang.Object@6e740a76
消費對象 java.lang.Object@697853f6
......
消費對象 java.lang.Object@41a10cbc
消費對象 java.lang.Object@4963c8d1
消費者 資源 隊列大小 5
生產者資源隊列大小= 5
生產者資源隊列大小= 5
消費者 資源 隊列大小 4
消費對象 java.lang.Object@3e49c35d
消費者 資源 隊列大小 4
從輸出結果中,我們可以發現隊列大小永遠不會超過5,消費者線程消費了生產者生產的資源。