生產者-消費者是多線程協作的經典模式:共享有界緩衝區,生產者放入、消費者取出。本文提供三個層次的實現:**基礎 wait/notify (教學用)、ArrayBlockingQueue (生產標準)、自定義條件變量 (進階控制)**,並深入解析 while 迴圈防虛假喚醒、鎖釋放時機、通知策略。
關鍵點解析
| 關鍵點 | 說明 |
|---|---|
synchronized 方法 | 鎖住 Buffer 實例,保證互斥。 |
while (condition) wait() | 必須用 while:虛假喚醒、多線程競爭、notifyAll 喚醒非目標線程,醒來需重新檢查條件。 |
wait() | 釋放鎖,線程進入對象的 **Wait Set (等待集)**,不佔用 CPU。 |
notifyAll() | 將 Wait Set 中所有線程移至 **Entry Set (入口集/就緒隊列)** 競爭鎖。notify() 僅喚醒一個,若喚醒同類 (生產喚醒生產) 會導致活鎖,故用 notifyAll。 |
class AdvancedBuffer {
private final int[] items = new int[10];
private int putIdx = 0, takeIdx = 0, count = 0;
private final ReentrantLock lock = new ReentrantLock();
private final Condition notFull = lock.newCondition();
private final Condition notEmpty = lock.newCondition();
public void put(int x) throws InterruptedException {
lock.lock();
try {
while (count == items.length) notFull.await(); // 精確等待「不滿」
items[putIdx] = x;
if (++putIdx == items.length) putIdx = 0;
++count;
notEmpty.signal(); // 精確通知「不空」
} finally { lock.unlock(); }
}
public int take() throws InterruptedException {
lock.lock();
try {
while (count == 0) notEmpty.await();
int x = items[takeIdx];
if (++takeIdx == items.length) takeIdx = 0;
--count;
notFull.signal();
return x;
} finally { lock.unlock(); }
}
}
優勢:可中斷鎖 (lockInterruptibly)、超時等待 (await(timeout))、多條件變量分離通知 (效能優於 notifyAll)、公平鎖選項。
| 主題 | 解決方案 |
|---|---|
| 優雅關閉 | 引入 volatile boolean shutdown,生產者檢查退出,消費者 poll(timeout) 定期檢查。 |
| 背壓傳播 | 生產者 offer(timeout) 失敗時觸發限流/丟棄/持久化策略 (Reactive Streams 規範)。 |
| 優先級消費 | PriorityBlockingQueue 或自定義 Condition 邏輯。 |
| 延遲消費 | DelayQueue (元素實作 Delayed)。 |
| 分佈式隊列 | Redis List (BLPOP)、RabbitMQ、Kafka、RocketMQ。 |
生產者-消費者模式核心在於**有界緩衝區 + 互斥 + 狀態等待/通知**。教學階段理解 wait/notify 機制 (while 迴圈、鎖釋放、Wait Set/Entry Set) 是並發編程內功;**生產代碼直接使用 ArrayBlockingQueue 或 LinkedBlockingQueue**,專注業務邏輯。進階場景靈活運用 ReentrantLock + Condition、CompletableFuture、分佈式訊息隊列。