在多線程生產者-消費者模型中,傳統集合配合 wait/notify 實現同步既繁瑣又易出錯。JDK 5.0 引入 java.util.concurrent 包,提供了線程安全、高效能的並發容器。本文聚焦 BlockingQueue 及其核心實現類,剖析源碼級設計細節。

核心實現類:

四組方法對應不同阻塞語義:
| 操作 | 拋出異常 | 返回布爾值 | 無限阻塞 | 超時阻塞 |
|---|---|---|---|---|
| 插入 | add(e) | offer(e) | put(e) | offer(e, time, unit) |
| 移除 | remove() | poll() | take() | poll(time, unit) |
| 檢查 | element() | peek() | - | - |
final Object[] items; // 存儲元素的數組
int takeIndex; // 下一次取元素的索引
int putIndex; // 下一次放元素的索引
int count; // 當前元素數量
final ReentrantLock lock; // 獨佔鎖(公平/非公平可選)
private final Condition notEmpty; // 非空條件
private final Condition notFull; // 非滿條件
// 1. offer(e) - 非阻塞,滿則返回 false
public boolean offer(E e) {
checkNotNull(e);
final ReentrantLock lock = this.lock;
lock.lock();
try {
if (count == items.length) return false;
insert(e);
return true;
} finally {
lock.unlock();
}
}
// 2. put(e) - 阻塞直到有空位
public void put(E e) throws InterruptedException {
checkNotNull(e);
final ReentrantLock lock = this.lock;
lock.lockInterruptibly();
try {
while (count == items.length)
notFull.await(); // 釋放鎖並進入等待
insert(e);
} finally {
lock.unlock();
}
}
// 3. offer(e, timeout, unit) - 定時阻塞
public boolean offer(E e, long timeout, TimeUnit unit)
throws InterruptedException {
// ... 使用 notFull.awaitNanos(nanos) 實現超時等待
}
private void insert(E x) {
items[putIndex] = x;
putIndex = inc(putIndex); // 環形增長:(++i == items.length) ? 0 : i
++count;
notEmpty.signal(); // 喚醒一個等待取元素的線程
}
private E extract() {
final E[] items = this.items;
E x = items[takeIndex];
items[takeIndex] = null; // GC 友好
takeIndex = inc(takeIndex);
--count;
notFull.signal(); // 喚醒一個等待放元素的線程
return x;
}
Condition 依賴 AbstractQueuedSynchronizer 內部類 ConditionObject。await() 流程:
fullyRelease)LockSupport.park 掛起線程signal() 喚醒後,重新競爭鎖(acquireQueued)這保證了 await/signal 必須在鎖保護下執行,享受重入特性。
Integer.MAX_VALUE(無界)putLock(寫)、takeLock(讀)notFull、notEmptyremove、toArray 等)按固定順序 putLock → takeLock 避免死鎖private final PriorityQueue<E> q; // 底層優先級隊列
private final ReentrantLock lock = new ReentrantLock(true); // 公平鎖
private final Condition notEmpty = lock.newCondition();
PriorityQueue,自然順序或自定義 Comparatorgrow()),無 notFull 條件元素必須實現 Delayed 接口(繼承 Comparable<Delayed>)。核心特性:
getDelay(NANOSECONDS) <= 0poll() 返回 null 而非阻塞;take() 阻塞直到有元素過期size() 包含未過期元素Condition available 僅服務 take/poll應用場景:ScheduledThreadPoolExecutor 內部使用 DelayQueue 管理定時任務。
BlockingDeque 繼承 BlockingQueue 和 DequeLinkedBlockingDeque 單鎖(lock)雙條件(notEmpty、notFull)所有實現類同時實現 Collection 接口,但 addAll、removeAll、retainAll、containsAll 等批量操作不保證原子性。例如向 ArrayBlockingQueue addAll 10 個元素,可能中途失敗但已部分入隊。解決方案:自行加鎖或使用單元素操作循環。
| 場景 | 推薦實現 | 關鍵特性 |
|---|---|---|
| 固定容量、高並發生產消費 | ArrayBlockingQueue | 環形數組、單鎖、公平性可選 |
| 大容量/無界、吞吐優先 | LinkedBlockingQueue | 雙鎖分離讀寫、鏈表動態擴容 |
| 優先級處理、任務調度 | PriorityBlockingQueue | 堆排序、無界、自動擴容 |
| 定時/延遲任務執行 | DelayQueue | 時間輪思想、過期才可取 |
| 雙端操作、工作竊取 | LinkedBlockingDeque | 首尾並行、單鎖 |