隊列是先進先出(FIFO)的數據結構,基本操作:尾部入隊、頭部出隊。阻塞隊列在普通隊列基礎上增加了線程阻塞語義:
take() 獲取元素操作阻塞,直到有元素入隊;put() 添加元素操作阻塞,直到有空位。這完美契合「生產者-消費者」模型,無需額外實現同步與喚醒策略。
| 隊列 | 底層結構 | 有界/無界 | 特點 |
|---|---|---|---|
| ArrayBlockingQueue | 數組 | 有界(構造時指定容量) | 默認非公平鎖,可選公平鎖 |
| LinkedBlockingQueue | 鏈表 | 有界(默認 Integer.MAX_VALUE) | 吞吐量通常優於數組隊列 |
| PriorityBlockingQueue | 堆(數組) | 無界 | 按優先級排序,非 FIFO |
| DelayQueue | 優先隊列 | 無界 | 元素延遲到期才能取出,生產者永不阻塞 |
| 方法 | 行為 | 阻塞性 |
|---|---|---|
put(E e) | 尾部插入,滿則阻塞等待 | 阻塞 |
take() | 頭部獲取並移除,空則阻塞等待 | 阻塞 |
offer(E e, timeout, unit) | 尾部插入,滿則等待指定時間,超時返回 false | 超時阻塞 |
poll(timeout, unit) | 頭部獲取,空則等待指定時間,超時返回 null | 超時阻塞 |
final Object[] items; // 底層數組
int takeIndex; // 隊頭索引
int putIndex; // 隊尾索引
int count; // 元素數量
final ReentrantLock lock; // 重入鎖
private final Condition notEmpty; // 非空條件
private final Condition notFull; // 非滿條件
public void put(E e) throws InterruptedException {
checkNotNull(e);
final ReentrantLock lock = this.lock;
lock.lockInterruptibly(); // 可中斷加鎖
try {
while (count == items.length) // 滿則等待
notFull.await();
enqueue(e); // 實際入隊
} finally {
lock.unlock();
}
}
private void enqueue(E x) {
final Object[] items = this.items;
items[putIndex] = x;
if (++putIndex == items.length) putIndex = 0; // 環形數組
count++;
notEmpty.signal(); // 喚醒消費者
}
public E take() throws InterruptedException {
final ReentrantLock lock = this.lock;
lock.lockInterruptibly();
try {
while (count == 0) // 空則等待
notEmpty.await();
return dequeue(); // 實際出隊
} finally {
lock.unlock();
}
}
private E dequeue() {
final Object[] items = this.items;
@SuppressWarnings("unchecked")
E x = (E) items[takeIndex];
items[takeIndex] = null; // GC 友好
if (++takeIndex == items.length) takeIndex = 0;
count--;
notFull.signal(); // 喚醒生產者
return x;
}
設計亮點:單鎖雙條件變量(notEmpty/notFull)、環形數組避免數據移動、可中斷鎖響應中斷。
public class ProducerConsumerDemo {
private static final int QUEUE_SIZE = 10;
private static final ArrayBlockingQueue queue = new ArrayBlockingQueue<>(QUEUE_SIZE);
public static void main(String[] args) {
new Thread(new Producer()).start();
new Thread(new Consumer()).start();
}
static class Producer extends Thread {
@Override public void run() {
while (true) {
try {
queue.put(1); // 生產
System.out.println("生產元素,剩餘空間:" + (QUEUE_SIZE - queue.size()));
Thread.sleep(500);
} catch (InterruptedException e) { e.printStackTrace(); }
}
}
}
static class Consumer extends Thread {
@Override public void run() {
while (true) {
try {
queue.take(); // 消費
System.out.println("消費元素,剩餘元素:" + queue.size());
Thread.sleep(1000);
} catch (InterruptedException e) { e.printStackTrace(); }
}
}
}
}
輸出示例:
生產元素,剩餘空間:9 生產元素,剩餘空間:8 ... 消費元素,剩餘元素:9 生產元素,剩餘空間:9 ...
offer/poll 帶超時版本,配合關閉標誌位,避免線程無限阻塞;阻塞隊列是 Java 並發包中解耦生產消費的核心組件。ArrayBlockingQueue 以環形數組+單鎖雙條件實現高效有界緩衝,LinkedBlockingQueue 適合高吞吐場景,PriorityBlockingQueue/DelayQueue 則服務於調度類業務。掌握其原理與 API,能讓你在高併發系統設計中遊刃有餘。