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

Java 多線程基礎:wait()/notify() 等待喚醒機制深度解析

     編輯:Java 相關知識

1. Object 類中的等待/喚醒 API

wait()、notify()、notifyAll() 定義在 Object.java 中:

  • wait():當前線程進入等待狀態,釋放持有的鎖,直到其他線程調用同對象的 notify() 或 notifyAll() 喚醒。
  • notify():喚醒在此對象監視器上等待的單個線程。
  • notifyAll():喚醒在此對象監視器上等待的所有線程。
  • 帶超時參數的重載:wait(long timeout)、wait(long timeout, int nanos)。

2. wait() 與 notify() 經典協作示例

// WaitTest.java
class ThreadA extends Thread {
    public ThreadA(String name) { super(name); }
    public void run() {
        synchronized (this) {
            System.out.println(Thread.currentThread().getName() + " call notify()");
            notify(); // 喚醒等待線程
        }
    }
}
public class WaitTest {
    public static void main(String[] args) {
        ThreadA t1 = new ThreadA("t1");
        synchronized (t1) {
            try {
                System.out.println(Thread.currentThread().getName() + " start t1");
                t1.start();
                System.out.println(Thread.currentThread().getName() + " wait()");
                t1.wait(); // 主線程等待
                System.out.println(Thread.currentThread().getName() + " continue");
            } catch (InterruptedException e) { e.printStackTrace(); }
        }
    }
}

運行結果

main start t1
main wait()
t1 call notify()
main continue

流程圖解

  1. 主線程通過 synchronized(t1) 獲取 t1 對象鎖。
  2. 啟動 t1 後,主線程調用 t1.wait() 釋放鎖並進入等待。
  3. t1 運行,獲取 this 鎖,調用 notify() 喚醒主線程。
  4. t1 結束釋放鎖,主線程重獲鎖繼續執行。

核心疑問:為什麼 t1.wait() 卻是主線程等待?

JDK 文檔明確指出:wait() 讓「當前線程」等待。「當前線程」是正在 CPU 上運行的線程。雖然語法是 t1.wait(),但調用位置在主線程中,主線程必須是運行狀態才能執行該代碼,因此等待的是主線程。

3. wait(long timeout) 超時喚醒示例

// WaitTimeoutTest.java
class ThreadA extends Thread {
    public ThreadA(String name) { super(name); }
    public void run() {
        System.out.println(Thread.currentThread().getName() + " run");
        while (true); // 死循環,不喚醒主線程
    }
}
public class WaitTimeoutTest {
    public static void main(String[] args) {
        ThreadA t1 = new ThreadA("t1");
        synchronized (t1) {
            try {
                System.out.println(Thread.currentThread().getName() + " start t1");
                t1.start();
                System.out.println(Thread.currentThread().getName() + " call wait");
                t1.wait(3000); // 等待 3 秒或被喚醒
                System.out.println(Thread.currentThread().getName() + " continue");
            } catch (InterruptedException e) { e.printStackTrace(); }
        }
    }
}

結果:約 3 秒後輸出 "main continue"

4. notifyAll() 喚醒所有等待線程

// NotifyAllTest.java
public class NotifyAllTest {
    private static Object obj = new Object();
    public static void main(String[] args) {
        ThreadA t1 = new ThreadA("t1");
        ThreadA t2 = new ThreadA("t2");
        ThreadA t3 = new ThreadA("t3");
        t1.start(); t2.start(); t3.start();
        try { Thread.sleep(3000); } catch (InterruptedException e) { e.printStackTrace(); }
        synchronized (obj) {
            System.out.println(Thread.currentThread().getName() + " notifyAll()");
            obj.notifyAll(); // 喚醒 t1, t2, t3
        }
    }
    static class ThreadA extends Thread {
        public ThreadA(String name) { super(name); }
        public void run() {
            synchronized (obj) {
                try {
                    System.out.println(Thread.currentThread().getName() + " wait");
                    obj.wait();
                    System.out.println(Thread.currentThread().getName() + " continue");
                } catch (InterruptedException e) { e.printStackTrace(); }
            }
        }
    }
}

結果:三線程全部被喚醒並繼續執行

5. 為什麼 wait/notify 定義在 Object 而非 Thread?

核心原因:它們依賴於「對象的同步鎖」。

  • wait() 讓線程等待並釋放鎖,否則其他線程無法獲取鎖進入同步塊調用 notify()。
  • notify() 必須在持有「同一把鎖」的前提下調用,才能喚醒因該鎖而等待的線程。
  • 鎖屬於對象,每個對象有且僅有一把鎖,因此這些方法屬於 Object。
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved