首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> Java 程式設計 >> Java 相關知識

深入理解 Java 鎖池與等待池:wait/notify 機制詳解

     編輯:Java 相關知識

核心概念

在 Java 多線程同步中,鎖池與等待池是兩個至關重要卻常被混淆的概念。它們共同構成了 synchronized、wait()、notify() 協作的運作基礎。

經典生產者-消費者模型實戰

以下代碼演示一個共享計數器,生產者遞增,消費者遞減,通過 wait/notify 實現精準協作:

public class Target {
    private int count = 0;

    // 生產者調用
    public synchronized void increase() {
        // 1. 條件不滿足,進入等待池
        if (count == 2) {
            try {
                wait(); // 釋放鎖,進入等待池
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        count++;
        System.out.println(Thread.currentThread().getName() + ": " + count);
        // 2. 唤醒等待池中的消費者
        notify(); 
    }

    // 消費者調用
    public synchronized void decrease() {
        if (count == 0) {
            try {
                wait(); // 釋放鎖,進入等待池
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        count--;
        System.out.println(Thread.currentThread().getName() + ": " + count);
        // 唤醒生產者
        notify();
    }
}

鎖池 vs 等待池:關鍵區別

維度鎖池等待池
觸發條件線程嘗試獲取 synchronized 鎖失敗線程調用 wait() 主動釋放鎖
線程狀態BLOCKED (阻塞)WAITING / TIMED_WAITING (等待)
持有鎖不持有鎖,排隊等待獲取已釋放鎖,不佔用鎖資源
喚醒方式持有鎖的線程釋放鎖 (退出同步塊)其他線程調用 notify()/notifyAll()
喚醒後去向直接嘗試獲取鎖,成功則繼續執行進入鎖池,重新競爭鎖

流程圖解:從等待池到鎖池

  1. 線程 A 持有鎖,調用 wait() → 釋放鎖 → 進入等待池。
  2. 線程 B 獲取鎖,執行完業務邏輯,調用 notify()。
  3. 線程 A 被喚醒 → 移出等待池 → 進入鎖池排隊。
  4. 線程 B 退出同步塊 → 釋放鎖。
  5. 線程 A 在鎖池中競爭成功 → 獲取鎖 → 從 wait() 後繼續執行。

關鍵細節與避坑指南

1. wait() 必須在同步塊中調用

否則拋出 IllegalMonitorStateException。因為只有持有鎖的線程才有資格釋放鎖並等待。

2. sleep() 不釋放鎖,wait() 釋放鎖

// 錯誤示範:sleep 不釋放鎖,其他線程無法進入
synchronized (obj) {
    Thread.sleep(1000); // 鎖依然被佔用!
}

// 正確:wait 釋放鎖,讓出 CPU 並允許其他線程進入
synchronized (obj) {
    obj.wait(1000); // 鎖被釋放,1秒後自動喚醒進入鎖池
}

3. notify() vs notifyAll()

  • notify():隨機喚醒一個等待線程,可能產生「信號丟失」問題(喚醒的線程不是預期的那個)。
  • notifyAll():喚醒所有等待線程,雖然會增加鎖競爭,但更安全,推薦在條件變量模式中使用。

4. 必須使用 while 循環檢查條件

// 標準範式:防止虛假喚醒
synchronized (lock) {
    while (conditionNotMet) {
        lock.wait();
    }
    // 執行業務邏輯
}

總結

鎖池是「想進去卻進不去」的隊列,等待池是「主動出去等通知」的休息區。wait() 是橋樑,notify() 是傳令兵。理解這兩個池的流轉關係,是掌握 Java 線程協作的核心鑰匙。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved