漫畫(huà):怎么證明Sleep不釋放鎖,而Wait釋放鎖?
wait 加鎖示例
public class WaitDemo {
private static Object locker = new Object();
public static void main(String[] args) throws InterruptedException {
WaitDemo waitDemo = new WaitDemo();
// 啟動(dòng)新線(xiàn)程,防止主線(xiàn)程被休眠
new Thread(() -> {
try {
waitDemo.doWait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}).start();
Thread.sleep(200); // 此行本身沒(méi)有意義,是為了確保 wait() 先執(zhí)行再執(zhí)行 notify()
waitDemo.doNotify();
}
/**
* 執(zhí)行 wait()
*/
private void doWait() throws InterruptedException {
synchronized (locker) {
System.out.println("wait start.");
locker.wait();
System.out.println("wait end.");
}
}
/**
* 執(zhí)行 notify()
*/
private void doNotify() {
synchronized (locker) {
System.out.println("notify start.");
locker.notify();
System.out.println("notify end.");
}
}
}
以上程序的執(zhí)行結(jié)果為:
- wait start.
- notify start.
- notify end.
- wait end.
代碼解析
從上述代碼可以看出,我們給 wait() 和 notify() 兩個(gè)方法上了同一把鎖(locker),但在調(diào)用完 wait() 方法之后 locker 鎖就被釋放了,所以程序才能正常執(zhí)行 notify() 的代碼,因?yàn)槭峭话焰i,如果不釋放鎖的話(huà),是不會(huì)執(zhí)行 notify() 的代碼的,這一點(diǎn)也可以從打印的結(jié)果中證實(shí)(結(jié)果輸出順序),所以綜合以上情況來(lái)說(shuō) wait() 方法是釋放鎖的。
sleep 加鎖示例
public class WaitDemo {
private static Object locker = new Object();
public static void main(String[] args) throws InterruptedException {
WaitDemo waitDemo = new WaitDemo();
// 啟動(dòng)新線(xiàn)程,防止主線(xiàn)程被休眠
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();
}
/**
* 執(zhí)行 notify()
*/
private void doNotify() {
synchronized (locker) {
System.out.println("notify start.");
locker.notify();
System.out.println("notify end.");
}
}
}
以上程序的執(zhí)行結(jié)果為:
- sleep start.
- sleep end.
- notify start.
- notify end.
代碼解析
從上述代碼可以看出 sleep(1000) 方法(行號(hào):11)執(zhí)行之后,調(diào)用 notify() 方法并沒(méi)有獲取到 locker 鎖,從上述執(zhí)行結(jié)果中可以看出,而是執(zhí)行完 sleep(1000) 方法之后才執(zhí)行的 notify() 方法,因此可以證明調(diào)用 sleep() 方法并不會(huì)釋放鎖。
知識(shí)擴(kuò)展
1.sleep 和 wait 有什么區(qū)別?
sleep 和 wait 幾乎是所有面試中必問(wèn)的題,但想完全回答正確似乎沒(méi)那么簡(jiǎn)單。
對(duì)于 sleep 和 wait 的區(qū)別,通常的回答是這樣的:
wait 必須搭配 synchronize 一起使用,而 sleep 不需要;
進(jìn)入 wait 狀態(tài)的線(xiàn)程能夠被 notify 和 notifyAll 線(xiàn)程喚醒,而 sleep 狀態(tài)的線(xiàn)程不能被 notify 方法喚醒;
wait 通常有條件地執(zhí)行,線(xiàn)程會(huì)一直處于 wait 狀態(tài),直到某個(gè)條件變?yōu)檎?,但?sleep 僅僅讓你的線(xiàn)程進(jìn)入睡眠狀態(tài);
wait 方法會(huì)釋放對(duì)象鎖,但 sleep 方法不會(huì)。
但上面的回答顯然遺漏了一個(gè)重要的區(qū)別,在調(diào)用 wait 方法之后,線(xiàn)程會(huì)變?yōu)?nbsp;WATING 狀態(tài),而調(diào)用 sleep 方法之后,線(xiàn)程會(huì)變?yōu)?nbsp;TIMED_WAITING 狀態(tài)。
2.wait 能不能在 static 方法中使用?為什么?
不能,因?yàn)?nbsp;wait 方法是實(shí)例方法(非 static 方法),因此不能在 static 中使用,源碼如下:
public final void wait() throws InterruptedException {
wait(0);
}
3.wait/notify 可以不搭配 synchronized 使用嗎?為什么?
不行,因?yàn)椴淮钆?nbsp;synchronized 使用的話(huà)程序會(huì)報(bào)錯(cuò),如下圖所示:
更深層次的原因是因?yàn)椴患?nbsp;synchronized 的話(huà)會(huì)造成 Lost Wake-Up Problem,喚醒丟失的問(wèn)題,詳情可見(jiàn):https://juejin.im/post/5e6a4d8a6fb9a07cd80f36d1
總結(jié)
本文我們通過(guò) synchronized 鎖定同一對(duì)象,來(lái)測(cè)試 wait 和 sleep 方法,再通過(guò)執(zhí)行結(jié)果的先后順序證明:wait 方法會(huì)釋放鎖,而 sleep 方法并不會(huì)。同時(shí)我們還講了幾個(gè) wait 和 sleep 的常見(jiàn)面試問(wèn)題,希望本文可以幫助到你。