如何證明sleep不釋放鎖,而wait釋放鎖?
wait 加鎖示例
public class WaitDemo {
private static Object locker = new Object();
public static void main(String[] args) throws InterruptedException {
WaitDemo waitDemo = new WaitDemo();
// 啟動新執行緒,防止主執行緒被休眠
new Thread(() -> {
try {
waitDemo.doWait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}).start();
Thread.sleep(200); // 此行本身沒有意義,是為了確保 wait() 先執行再執行 notify()
waitDemo.doNotify();
}
/**
* 執行 wait()
*/
private void doWait() throws InterruptedException {
synchronized (locker) {
System.out.println("wait start.");
locker.wait();
System.out.println("wait end.");
}
}
/**
* 執行 notify()
*/
private void doNotify() {
synchronized (locker) {
System.out.println("notify start.");
locker.notify();
System.out.println("notify end.");
}
}
}
以上程式的執行結果為:
wait start.
notify start.
notify end.
wait end.
程式碼解析
從上述程式碼可以看出,我們給 wait()
和 notify()
兩個方法上了同一把鎖(locker),但在呼叫完 wait()
方法之後 locker
鎖就被釋放了,所以程式才能正常執行 notify()
的程式碼,因為是同一把鎖,如果不釋放鎖的話,是不會執行 notify()
的程式碼的,這一點也可以從列印的結果中證實(結果輸出順序),所以綜合以上情況來說 wait()
方法是釋放鎖的。
sleep 加鎖示例
public class WaitDemo {
private static Object locker = new Object();
public static void main(String[] args) throws InterruptedException {
WaitDemo waitDemo = new WaitDemo();
// 啟動新執行緒,防止主執行緒被休眠
new Thread(() -> {
synchronized (locker) {
try {
System.out.println("sleep start.");
Thread.sleep(1000);
System.out.println("sleep end.");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}).start();
Thread.sleep(200);
waitDemo.doNotify();
}
/**
* 執行 notify()
*/
private void doNotify() {
synchronized (locker) {
System.out.println("notify start.");
locker.notify();
System.out.println("notify end.");
}
}
}
以上程式的執行結果為:
sleep start.
sleep end.
notify start.
notify end.
程式碼解析
從上述程式碼可以看出 sleep(1000)
方法(行號:11)執行之後,呼叫 notify()
方法並沒有獲取到 locker 鎖,從上述執行結果中可以看出,而是執行完 sleep(1000)
方法之後才執行的 notify()
方法,因此可以證明呼叫 sleep()
方法並不會釋放鎖。
知識擴充套件
1.sleep 和 wait 有什麼區別?
sleep
和 wait
幾乎是所有面試中必問的題,但想完全回答正確似乎沒那麼簡單。
對於 sleep
和 wait
的區別,通常的回答是這樣的:
- wait 必須搭配 synchronize 一起使用,而 sleep 不需要;
- 進入 wait 狀態的執行緒能夠被 notify 和 notifyAll 執行緒喚醒,而 sleep 狀態的執行緒不能被 notify 方法喚醒;
- wait 通常有條件地執行,執行緒會一直處於 wait 狀態,直到某個條件變為真,但是 sleep 僅僅讓你的執行緒進入睡眠狀態;
- wait 方法會釋放物件鎖,但 sleep 方法不會。
但上面的回答顯然遺漏了一個重要的區別,在呼叫 wait
方法之後,執行緒會變為 WATING
狀態,而呼叫 sleep
方法之後,執行緒會變為 TIMED_WAITING
狀態。
2.wait 能不能在 static 方法中使用?為什麼?
不能,因為 wait
方法是例項方法(非 static
方法),因此不能在 static
中使用,原始碼如下:
public final void wait() throws InterruptedException {
wait(0);
}
3.wait/notify 可以不搭配 synchronized 使用嗎?為什麼?
不行,因為不搭配 synchronized
使用的話程式會報錯,如下圖所示:
更深層次的原因是因為不加 synchronized
的話會造成 Lost Wake-Up Problem,喚醒丟失的問題,詳情可見:https://juejin.im/post/5e6a4d8a6fb9a07cd80f36d1
總結
本文我們通過 synchronized
鎖定同一物件,來測試 wait
和 sleep
方法,再通過執行結果的先後順序證明:wait
方法會釋放鎖,而 sleep
方法並不會。同時我們還講了幾個 wait
和 sleep
的常見面試問題,希望本文可以幫助到你。