synchronized + wait/notify 是舊世代協作方式,缺點:單一條件隊列、無法中斷等待、無公平鎖選擇。ReentrantLock + Condition 提供:多條件變量(notFull/notEmpty)、可中斷等待、公平/非公平鎖、等待超時,是生產者-消費者的標準現代實作。
class BoundedBuffer {
private final List<Object> buffer = new ArrayList<>();
private final int capacity;
private final Lock lock = new ReentrantLock(true); // 公平鎖
private final Condition notFull = lock.newCondition(); // 生產者等待
private final Condition notEmpty = lock.newCondition(); // 消費者等待
public void produce() {
lock.lock();
try {
while (buffer.size() == capacity) notFull.await(); // 滿了等待
buffer.add(new Object());
notEmpty.signalAll(); // 喚醒消費者
} catch (InterruptedException e) { Thread.currentThread().interrupt(); }
finally { lock.unlock(); }
}
public void consume() {
lock.lock();
try {
while (buffer.isEmpty()) notEmpty.await(); // 空了等待
buffer.remove(0);
notFull.signalAll(); // 喚醒生產者
} catch (InterruptedException e) { Thread.currentThread().interrupt(); }
finally { lock.unlock(); }
}
}
| 特性 | synchronized + wait/notify | Lock + Condition |
|---|---|---|
| 條件隊列 | 單一(物件鎖綁定) | 多個(newCondition()) |
| 等待可中斷 | 不可(只能 notify) | await() 拋 InterruptedException |
| 公平鎖 | 不支援 | new ReentrantLock(true) |
| 嘗試獲取鎖 | 不支援 | tryLock(timeout) |
| 等待超時 | wait(timeout) 易誤用 | await(timeout, TimeUnit) 精確 |
| 程式碼結構 | 同步塊分散 | 鎖/條件物件化,職責清晰 |
while (condition) await() 防止虛假喚醒。lock.unlock() 必須在 finally,否則死鎖。InterruptedException 後重設中斷旗標 Thread.currentThread().interrupt(),讓上層感知。signalAll() 避免信號遺漏;單生產單消費可用 signal() 提升效能。LinkedBlockingQueue 直接替代手寫。BlockingQueue<Task> queue = new ArrayBlockingQueue<>(capacity);
// 生產者:queue.put(task); // 滿阻塞
// 消費者:Task t = queue.take(); // 空阻塞
除非教學、極度定制化需求,優先使用 BlockingQueue 實作,經過嚴格測試、效能優化、功能完整(剩餘容量、優先級、遲緩提交等)。