如何證明sleep不釋放鎖,而wait釋放鎖?
阿新 • • 發佈:2020-07-21
![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` 的常見面試問題,希望本文可以幫助