Java 中的 Wait 和 Notify 機制

寫在前面

WaitNotifyJava 面試中常見的問題,可是在平時工做中可能不常見到。你們或多或少知道些背景知識,例如兩者均爲 Object 類的方法,而不是 Thread 特有的(由於是每一個對象都具備的特性,所以操做鎖的方法也緊跟對象,沒毛病),且都只能在同步代碼塊中調用(即前提是先得到對象的監視器鎖,通常來講在 synchronized 代碼塊中使用),不然拋出異常 IllegalMonitorStateExceptionjava

Wait 會掛起本身讓出 CPU 時間片,並將自身加入鎖定對象的 Wait Set 中,釋放對象的監視器鎖(monitor)讓其餘線程能夠得到,直到其餘線程調用此對象的 notify( ) 方法或 notifyAll( ) 方法,自身才能被喚醒(這裏有個特殊狀況就是 Wait 能夠增長等待時間);Notify 方法則會釋放監視器鎖的同時,喚醒對象 Wait Set 中等待的線程,順序是隨機的不肯定。程序員

Wait Set

虛擬機規範中定義了一個 Wait Set 的概念,但至於其具體是什麼樣的數據結構規範沒有強制規定,意味着不一樣的廠商能夠自行實現,但無論怎樣,線程調用了某個對象的 Wait 方法,就會被加入該對象的 Wait Set面試

在這裏插入圖片描述

Demo 代碼

下面經過一段 demo 來解釋 WaitNotify 的功能shell

import java.util.concurrent.TimeUnit;

public class WaitNotify {

    public static void main(String[] args) {

        final Object A = new Object();
        final Object B = new Object();

        Thread t1 = new Thread("t1-thread") {
            @Override
            public void run() {
                synchronized (A) {
                    System.out.println(Thread.currentThread().getName() + "拿到 A 的監視器鎖");
                    System.out.println(Thread.currentThread().getName() + "嘗試獲取 B 的監視器鎖");
                    try {
                        System.out.println(Thread.currentThread().getName() + "休眠 2s,不釋放 A 的監視器鎖");
                        TimeUnit.SECONDS.sleep(2);
                        System.out.println(Thread.currentThread().getName() + "掛起本身,釋放 A 的監視器鎖");
                        A.wait();
                        System.out.println(Thread.currentThread().getName() + "被喚醒,等待獲取 B 的監視器鎖");
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    synchronized (B) {
                        System.out.println(Thread.currentThread().getName() + "拿到 B 的監視器鎖");
                        B.notify();
                    }
                }
            }
        };

        Thread t2 = new Thread("t2-thread") {
            @Override
            public void run() {
                synchronized (B) {
                    System.out.println(Thread.currentThread().getName() + "拿到 B 的監視器鎖");
                    System.out.println(Thread.currentThread().getName() + "嘗試獲取 A 的監視器鎖");
                    synchronized (A) {
                        System.out.println(Thread.currentThread().getName() + "拿到 A 的監視器鎖");
                        try {
                            System.out.println(Thread.currentThread().getName() + "休眠 2s,不釋放 A 的監視器鎖");
                            TimeUnit.SECONDS.sleep(2);
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                        System.out.println(Thread.currentThread().getName() + "掛起本身,釋放 A 的監視器鎖,喚醒 t0");
                        A.notify();
                    }
                    try {
                        System.out.println(Thread.currentThread().getName() + "休眠 2s,不釋放 B 的監視器鎖");
                        TimeUnit.SECONDS.sleep(2);
                        System.out.println(Thread.currentThread().getName() + "掛起本身,釋放 B 的監視器鎖");
                        B.wait();
                        System.out.println(Thread.currentThread().getName() + "被喚醒");
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }
        };

        t1.start();
        t2.start();
    }
}
複製代碼

輸出結果

t1-thread拿到 A 的監視器鎖
t2-thread拿到 B 的監視器鎖
t1-thread嘗試獲取 B 的監視器鎖
t2-thread嘗試獲取 A 的監視器鎖
t1-thread休眠 2s,不釋放 A 的監視器鎖
t1-thread掛起本身,釋放 A 的監視器鎖
t2-thread拿到 A 的監視器鎖
t2-thread休眠 2s,不釋放 A 的監視器鎖
t2-thread掛起本身,釋放 A 的監視器鎖,喚醒 t0
t2-thread休眠 2s,不釋放 B 的監視器鎖
t1-thread被喚醒,等待獲取 B 的監視器鎖
t2-thread掛起本身,釋放 B 的監視器鎖
t1-thread拿到 B 的監視器鎖
t2-thread被喚醒

Process finished with exit code 0
複製代碼

時序圖

在這裏插入圖片描述

寫在最後

這是一個不定時更新的、披着程序員外衣的文青小號,歡迎關注。數據結構

相關文章
相關標籤/搜索