1. 程式人生 > >如何證明sleep不釋放鎖,而wait釋放鎖?

如何證明sleep不釋放鎖,而wait釋放鎖?

![1.png](https://cdn.nlark.com/yuque/0/2020/png/92791/1595295260068-a5b2b884-ce6e-4854-95c2-3c8663fd948e.png#align=left&display=inline&height=388&margin=%5Bobject%20Object%5D&name=1.png&originHeight=388&originWidth=678&size=96413&status=done&style=none&width=678)![2.png](https://cdn.nlark.com/yuque/0/2020/png/92791/1595295260943-0b05e404-e447-4c2c-9a61-7203afb90e82.png#align=left&display=inline&height=388&margin=%5Bobject%20Object%5D&name=2.png&originHeight=388&originWidth=678&size=94552&status=done&style=none&width=678)![3.png](https://cdn.nlark.com/yuque/0/2020/png/92791/1595295261877-a4d29aaf-c9b2-47d5-aac2-269e118146fe.png#align=left&display=inline&height=388&margin=%5Bobject%20Object%5D&name=3.png&originHeight=388&originWidth=678&size=93520&status=done&style=none&width=678)![4.png](https://cdn.nlark.com/yuque/0/2020/png/92791/1595295262808-6353dfdd-d229-4788-a6a4-06f291c23c6e.png#align=left&display=inline&height=388&margin=%5Bobject%20Object%5D&name=4.png&originHeight=388&originWidth=678&size=103822&status=done&style=none&width=678)![5.png](https://cdn.nlark.com/yuque/0/2020/png/92791/1595295264038-5fd233e4-6cf3-4f11-97d1-008e77780ef4.png#align=left&display=inline&height=388&margin=%5Bobject%20Object%5D&name=5.png&originHeight=388&originWidth=678&size=102763&status=done&style=none&width=678)![6.png](https://cdn.nlark.com/yuque/0/2020/png/92791/1595295264845-1ee63782-0ff1-4eda-a206-c23a227b8d22.png#align=left&display=inline&height=388&margin=%5Bobject%20Object%5D&name=6.png&originHeight=388&originWidth=678&size=92707&status=done&style=none&width=678)![7.png](https://cdn.nlark.com/yuque/0/2020/png/92791/1595295265597-8afb9fc5-42ff-46d3-a5b5-bfb940867a64.png#align=left&display=inline&height=388&margin=%5Bobject%20Object%5D&name=7.png&originHeight=388&originWidth=678&size=81660&status=done&style=none&width=678) ## wait 加鎖示例 ```java 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 加鎖示例 ```java 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` 中使用,原始碼如下: ```java public final void wait() throws InterruptedException { wait(0); } ``` ### 3.wait/notify 可以不搭配 synchronized 使用嗎?為什麼? 不行,因為不搭配 `synchronized` 使用的話程式會報錯,如下圖所示: ![image.png](https://cdn.nlark.com/yuque/0/2020/png/92791/1595251664814-ca5ba6ef-4fda-4fdc-8270-2159136c813b.png#align=left&display=inline&height=225&margin=%5Bobject%20Object%5D&name=image.png&originHeight=450&originWidth=1748&size=94065&status=done&style=none&width=874) 更深層次的原因是因為不加 `synchronized` 的話會造成 Lost Wake-Up Problem,喚醒丟失的問題,詳情可見:[https://juejin.im/post/5e6a4d8a6fb9a07cd80f36d1](https://juejin.im/post/5e6a4d8a6fb9a07cd80f36d1) ## 總結 本文我們通過 `synchronized` 鎖定同一物件,來測試 `wait` 和 `sleep` 方法,再通過執行結果的先後順序證明:**`wait` 方法會釋放鎖,而 `sleep` 方法並不會**。同時我們還講了幾個 `wait` 和 `sleep` 的常見面試問題,希望本文可以幫助