| 同步模型 | 適用場景 | 核心機制 | 限制 |
|---|---|---|---|
| 同步鎖 | 互斥存取共享資源 | synchronized / Lock | 單一線程執行,無條件協作 |
| 信號量 | 條件協作、生產消費、讀寫分離 | wait/notify / Condition | 需配合鎖使用,邏輯較複雜 |
Java 無專門 Semaphore/Mutex 物件(JDK 5+ 提供 java.util.concurrent.Semaphore),但任意物件皆可作為鎖與信號量:
// 同一物件既是鎖,也是信號量
Object signal = new Object();
// 等待通知
synchronized (signal) {
signal.wait(); // 釋放鎖,進入等待隊列
}
// 發送通知
synchronized (signal) {
signal.notify(); // 喚醒一個等待線程
}
signal.wait(); // 無限等待,直到被 notify
signal.wait(5000); // 最多等待 5 秒,超時自動喚醒
signal.wait(5000, 500000); // 納秒級精度(實際取決於 OS)
關鍵語意:
synchronized 內)synchronized (lock) {
while (!condition) { // 迴圈而非 if!
lock.wait();
}
// 條件滿足,執行業務邏輯
}
| 方法 | 行為 | 適用場景 | 風險 |
|---|---|---|---|
| notify() | 隨機喚醒一個等待線程 | 所有等待者處理邏輯相同(如連線池) | 喚醒錯誤線程導致「信號丟失」 |
| notifyAll() | 喚醒所有等待線程 | 等待條件不同、或為保險起見 | 驚群效應,效能較差 |
public class BoundedBuffer {
private final List buffer = new ArrayList<>();
private final int capacity;
public BoundedBuffer(int capacity) {
this.capacity = capacity;
}
// 生產者呼叫
public synchronized void put(T item) throws InterruptedException {
while (buffer.size() == capacity) {
wait(); // 緩衝區滿,等待消費者
}
buffer.add(item);
notifyAll(); // 喚醒消費者
}
// 消費者呼叫
public synchronized T take() throws InterruptedException {
while (buffer.isEmpty()) {
wait(); // 緩衝區空,等待生產者
}
T item = buffer.remove(0);
notifyAll(); // 喚醒生產者
return item;
}
}
BoundedBuffer buffer = new BoundedBuffer<>(5);
// 生產者
new Thread(() -> {
for (int i = 0; i < 100; i++) {
buffer.put(i);
System.out.println("生產: " + i);
sleep(100);
}
}, "Producer").start();
// 消費者
new Thread(() -> {
for (int i = 0; i < 100; i++) {
Integer item = buffer.take();
System.out.println("消費: " + item);
sleep(150);
}
}, "Consumer").start();
BlockingQueue queue = new ArrayBlockingQueue<>(5);
// 生產者
queue.put(item); // 滿時自動阻塞
// 消費者
Integer item = queue.take(); // 空時自動阻塞
優點:無需手寫 wait/notify、支援超時、可中斷、公平策略、內建生產消費語意。
ReentrantLock lock = new ReentrantLock();
Condition notFull = lock.newCondition();
Condition notEmpty = lock.newCondition();
lock.lock();
try {
while (buffer.size() == capacity) notFull.await();
buffer.add(item);
notEmpty.signal();
} finally {
lock.unlock();
}
優點:一個鎖支援多條件變數、可中斷等待、公平/非公平可選。
Semaphore permits = new Semaphore(5); // 5 個許可證
// 獲取許可
permits.acquire();
// 執行受限操作
permits.release();
適用:連線池、限流、資源存取控制。
| 錯誤寫法 | 問題 | 正確寫法 |
|---|---|---|
if (empty) wait(); | 虛假喚醒導致條件不成立仍執行 | while (empty) wait(); |
| 非 synchronized 內呼叫 wait() | IllegalMonitorStateException | 必須在 synchronized(lock) 內 |
| notify() 後立即釋放鎖 | 被喚醒線程無法立即獲得鎖 | 在 synchronized 區塊結束前呼叫 notify() |
| wait() 未處理 InterruptedException | 中斷狀態丟失,線程無法優雅停止 | 拋出或重設中斷旗標 |
Java 信號量模型核心是 wait/notify 機制,本質是「鎖 + 條件隊列」。雖語法簡單,但正確使用需遵守嚴格規範(迴圈檢查、同步塊、異常處理)。現代開發強烈建議直接使用 BlockingQueue、Condition、Semaphore 等高層抽象,它們封裝了經驗證的最佳實踐,減少併發 Bug 風險,提升程式碼可讀性與維護性。