wait()、notify()、notifyAll() 是 Object 的 final 方法,**必須在同步上下文中調用** (同步方法或 `synchronized` 代碼塊),否則拋出 IllegalMonitorStateException。它們操作的是當前同步鎖對象的等待集。
| 方法 | 行為 | 鎖狀態 | 線程狀態變遷 | 喚醒後動作 |
|---|---|---|---|---|
wait() | 釋放鎖,進入等待集,無限期阻塞 | 釋放 | RUNNABLE → WAITING | 進入鎖池競爭鎖,獲取後從 wait() 返回 |
wait(long timeout) | 釋放鎖,超時自動喚醒 | 釋放 | RUNNABLE → TIMED_WAITING | 同上,或超時後進入鎖池 |
notify() | 隨機喚醒一個等待線程 | 持有 (調用者仍持有鎖) | WAITING → BLOCKED (進入鎖池) | 喚醒線程在鎖池排隊 |
notifyAll() | 喚醒所有等待線程 | 持有 | WAITING → BLOCKED | 所有喚醒線程在鎖池競爭 |
public class WaitNotifyDemo {
// 共享資源:容量為 1 的緩衝區
private static final Object lock = new Object();
private static Integer buffer = null; // null 表示空
public static void main(String[] args) {
new Thread(WaitNotifyDemo::producer, "Producer").start();
new Thread(WaitNotifyDemo::consumer, "Consumer").start();
}
// 生產者
static void producer() {
for (int i = 1; i <= 10; i++) {
synchronized (lock) {
// 1. 緩衝區滿,等待
while (buffer != null) {
try {
System.out.println(Thread.currentThread().getName() + " 緩衝區滿,等待...");
lock.wait(); // 釋放鎖,進入等待集
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
return;
}
}
// 2. 生產數據
buffer = i;
System.out.println(Thread.currentThread().getName() + " 生產: " + buffer);
// 3. 唤醒消費者
lock.notifyAll(); // 推薦 notifyAll 避免信號丟失
}
// 模擬生產耗時
sleepRandom(100);
}
}
// 消費者
static void consumer() {
for (int i = 1; i <= 10; i++) {
synchronized (lock) {
// 1. 緩衝區空,等待
while (buffer == null) {
try {
System.out.println(Thread.currentThread().getName() + " 緩衝區空,等待...");
lock.wait();
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
return;
}
}
// 2. 消費數據
System.out.println(Thread.currentThread().getName() + " 消費: " + buffer);
buffer = null;
// 3. 唤醒生產者
lock.notifyAll();
}
sleepRandom(150);
}
}
static void sleepRandom(int max) {
try { Thread.sleep((long) (Math.random() * max)); } catch (InterruptedException ignored) {}
}
}
// ❌ 錯誤:if 檢查,虛假喚醒/多線程競爭下會出錯
if (buffer != null) lock.wait();
// ✅ 正確:while 循環檢查,防禦性編程
while (buffer != null) lock.wait();
三大風險:
| 維度 | wait() | sleep() |
|---|---|---|
| 所屬類 | Object | Thread |
| 鎖行為 | 釋放鎖 | 不釋放鎖 |
| 喚醒方式 | notify/notifyAll / 中斷 / 超時 | 超時 / 中斷 |
| 調用前提 | 必須持有鎖 (同步塊中) | 無前提,任意位置 |
| 典型用途 | 線程協作、條件等待 | 定時延遲、節流 |
JDK 1.5+ `java.util.concurrent.locks` 提供更強大、更靈活的協作機制:
Lock lock = new ReentrantLock();
Condition notFull = lock.newCondition();
Condition notEmpty = lock.newCondition();
// 生產者
lock.lock();
try {
while (buffer != null) notFull.await(); // 等待不滿
buffer = value;
notEmpty.signal(); // 喚醒消費者
} finally { lock.unlock(); }
優勢:一把鎖可關聯多個 Condition (精準喚醒)、支持公平鎖、可中斷鎖獲取、超時獲取、條件隊列可檢查。
wait/notify 是 Java 線程協作的**原語級基礎**,理解其「釋放鎖→等待→喚醒→搶鎖→繼續」的完整生命周期,配合 **while 循環檢查 + notifyAll** 範式,是寫正確併發代碼的必修內功。現代開發推薦使用 `Lock + Condition` 或更高級併發組件 (`BlockingQueue`, `CompletableFuture`, `StampedLock`),減少底層同步錯誤風險。