Wait
和 Notify
是 Java
面試中常見的問題,可是在平時工做中可能不常見到。你們或多或少知道些背景知識,例如兩者均爲 Object
類的方法,而不是 Thread
特有的(由於鎖
是每一個對象都具備的特性,所以操做鎖的方法也緊跟對象,沒毛病),且都只能在同步代碼塊
中調用(即前提是先得到對象的監視器鎖
,通常來講在 synchronized
代碼塊中使用),不然拋出異常 IllegalMonitorStateException
。java
Wait
會掛起本身讓出 CPU
時間片,並將自身加入鎖定對象的 Wait Set
中,釋放對象的監視器鎖(monitor)
讓其餘線程能夠得到,直到其餘線程調用此對象的 notify( )
方法或 notifyAll( )
方法,自身才能被喚醒(這裏有個特殊狀況就是 Wait
能夠增長等待時間);Notify
方法則會釋放監視器鎖的同時,喚醒對象 Wait Set
中等待的線程,順序是隨機的不肯定。程序員
虛擬機規範
中定義了一個 Wait Set
的概念,但至於其具體是什麼樣的數據結構規範沒有強制規定,意味着不一樣的廠商能夠自行實現,但無論怎樣,線程調用了某個對象的 Wait
方法,就會被加入該對象的 Wait Set
中面試
下面經過一段 demo
來解釋 Wait
和 Notify
的功能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
複製代碼
這是一個不定時更新的、披着程序員外衣的文青小號,歡迎關注。數據結構