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


1.png2.png3.png4.png5.png6.png7.png

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 有什么區別?

sleepwait 幾乎是所有面試中必問的題,但想完全回答正確似乎沒那么簡單。

對於 sleepwait 的區別,通常的回答是這樣的:

  • 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 使用的話程序會報錯,如下圖所示:
image.png

更深層次的原因是因為不加 synchronized 的話會造成 Lost Wake-Up Problem,喚醒丟失的問題,詳情可見:https://juejin.im/post/5e6a4d8a6fb9a07cd80f36d1

總結

本文我們通過 synchronized 鎖定同一對象,來測試 wait 和 sleep 方法,再通過執行結果的先后順序證明:wait 方法會釋放鎖,而 sleep 方法並不會。同時我們還講了幾個 waitsleep 的常見面試問題,希望本文可以幫助到你。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM