wait()、notify()方法詳解

簡介

wait()notify() 方法可用於控制線程的生命週期

詳解

1、wait() 不帶參數

當前線程 中調用 A對象wait() 方法,此時 當前線程 會進入 等待狀態,此時 當前線程 會釋放所持有的 鎖資源,等到 其它線程 調用 A對象notify() 或者 notifyAll() 進行喚醒,當前線程 纔有可能重新拿到 鎖資源 並繼續執行。

a、調用 wait() 與 notify() 的不是同一個對象-錯誤

/**
 * 調用 wait()、notify() 的不是同一個對象
 */
public class WaitDetail01Main {
    public static void main(String[] args) {
        WaitDetail01 t1 = new WaitDetail01();
        WaitDetail01 t2 = new WaitDetail01();
        t1.setFlag(true);
        t2.setFlag(false);
        t1.setName("A");
        t2.setName("B");
        t1.start();
        t2.start();
    }
}

class WaitDetail01 extends Thread{
    static Object o1 = new Object();
    private boolean flag;

    public void setFlag(boolean flag) {
        this.flag = flag;
    }

    @Override
    public void run() {
        if(flag){
            synchronized (this){
                try {
                    // 當前 this 爲 A線程對象
                    // 線程B對象調用 notify() 不會喚醒 線程A
                    // 一直等待
                    this.wait();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println("線程 " + Thread.currentThread().getName() + " 進入 1...");
            }
        }else{
            // B線程
            synchronized (this){
                System.out.println("線程 " + Thread.currentThread().getName() + " 進入 2...");
                // 當前 this 爲 線程B對象
                this.notify();
            }
        }
    }
}

解析:調用 wait()notify() 的必須是 同一個對象

b、當前線程未持有鎖對象,去調用 wait()、notify()-錯誤

/**
 * 當前線程未持有鎖,去調用 wait()、notify()
 */
public class WaitDetail02Main {
    public static void main(String[] args) {
        WaitDetail02 t1 = new WaitDetail02();
        WaitDetail02 t2 = new WaitDetail02();
        t1.setFlag(true);
        t2.setFlag(false);
        t1.setName("A");
        t2.setName("B");
        t1.start();
        t2.start();
    }
}

class WaitDetail02 extends Thread{
    static Object o1 = new Object();
    private boolean flag;

    public void setFlag(boolean flag) {
        this.flag = flag;
    }

    @Override
    public void run() {
        if(flag){
            try {
                // A線程
                // 未持有 o1 鎖對象,調用 wait()
                // 拋出 IllegalMonitorStateException
                o1.wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            synchronized (o1){
                System.out.println("線程 " + Thread.currentThread().getName() + " 進入 1...");
            }
        }else{
            // B線程
            // 未持有 o1 鎖對象,調用 notify()
            // 拋出 IllegalMonitorStateException
            o1.notify();
            synchronized (o1){
                System.out.println("線程 " + Thread.currentThread().getName() + " 進入 2...");
            }
        }
    }
}

解析:在當前線程中調用 wait()notify() 方法之前,必須保證當前線程 此時 已經持有了 鎖對象,否則拋出 IllegalMonitorStateException


2、wait() 帶參數

wait(long timeout) 帶一個參數:指定一個超時時間,在 當前線程 中調用 A對象wait(50) 方法,此時 當前線程 會進入 等待狀態,此時 當前線程 會釋放所持有的 鎖資源,等到 50毫秒 時間到了之後,自動被喚醒,纔有可能重新拿到 鎖資源 並繼續執行。

a、wait() 帶參數-正確

/**
 * wait() 帶參數
 */
public class WaitDetail03Main {
    public static void main(String[] args) {
        WaitDetail03 t1 = new WaitDetail03();
        WaitDetail03 t2 = new WaitDetail03();
        t1.setFlag(true);
        t2.setFlag(false);
        t1.setName("A");
        t2.setName("B");
        t1.start();
        t2.start();
    }
}

class WaitDetail03 extends Thread{
    static Object o1 = new Object();
    static Object o2 = new Object();
    private boolean flag;

    public void setFlag(boolean flag) {
        this.flag = flag;
    }

    @Override
    public void run() {
        if(flag){
            synchronized (o1){
                try {
                    // 等待 50 毫秒後 自動喚醒
                    // 50 毫秒後,B線程已經執行完畢並釋放了鎖資源 o1
                    // 此時 線程A 被自動喚醒,並重新獲取鎖資源 o1
                    o1.wait(50);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println("線程 " + Thread.currentThread().getName() + " 進入 1...");
            }
        }else{
            synchronized (o1){
                System.out.println("線程 " + Thread.currentThread().getName() + " 進入 2...");
            }
        }
    }
}

解析:wait() 帶參數的方法不需要滿足:wait()notify() 的調用者是同一個對象,因爲它不會 顯示調用 notify();但還是必須滿足:在當前線程中調用 wait() 帶參數的方法之前,必須保證當前線程 此時 已經持有了 鎖對象,否則拋出 IllegalMonitorStateException


3、notify()

隨機喚醒某個因爲執行了 wait() 方法而在 等待區 等待的線程

4、notifyAll()

喚醒所有因爲執行了 wait() 方法而在 等待區 等待的線程

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章