開發與維運

java多線程中sleep和wait的4個區別,你知道幾個?

一、sleep是線程方法,wait是Object方法


這個如何驗證呢?我們還需要到jdk源碼中看看。首先進入到Thread的源碼中看一下,然後俺ctrl+O就可以查看方法列表。在最上面可以搜尋,我們輸入“s”,就可以查看所有以s開頭的方法了。

v2-fca8c86c54edcf74a3e54f1529815959_1440w.jpg我們會發現,slee方法真實的在Thread線程類中。下面我們以同樣的方法查看wait。

v2-60c7dc0f1755ccb54d46e5648433d767_1440w.jpg

這是第一個區別很容易驗證,下面我們來看第二個。

二、sleep不釋放lock,wait會釋放


這個如何驗證呢?這就需要代碼了。先看我們的sleep方法

public class Test {
    private final static Object lock = new Object();
    public static void main(String[] args) {
        Stream.of("線程1","線程2").forEach(n->new Thread(n) {
            public void run(){
                Test.testSleep();
            }
        }.start());
    }
    //sleep方法休眠之後,
    private static void testSleep() {
        synchronized (lock) {
            try {
                System.out.println(Thread.currentThread().getName()
                                   +"正在執行");
                Thread.sleep(10_000);
                System.out.println(Thread.currentThread().getName()
                                   +"休眠結束");
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

我們看一下運行結果:

v2-5d60e4351cfca47e4161539e6891c70d_1440w.png

在上面的結果中,線程2先獲取了cpu資源,然後開始執行休眠,在休眠過程中線程1是沒法執行的,必須要等待線程2結束之後才可以。這也就是說sleep方法不會釋放鎖,讓其他線程進來。

然後我們測試一下wait方法。

public class Test {
    private final static Object lock = new Object();
    public static void main(String[] args) {
        Stream.of("線程1", "線程2").forEach(n -> new Thread(n) {
            public void run() {
                Test.testWait();
            }
        }.start());
    }
    private static void testWait() {
        synchronized (lock) {
            try {
                System.out.println(Thread.currentThread().getName()
                                   + "正在執行");
                lock.wait(10_000);
                System.out.println(Thread.currentThread().getName()
                                   + "wait結束");
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

在上面的例子中,我們使用wait方法等待10秒鐘,然後結束。我們看一下結果:

v2-d99984ef90846c7d0aac6c400a97eaca_1440w.png

這個過程就驗證了第二條區別,我們接下來看第三個。

三、sleep不依賴同步方法,wait需要


我們還是依次來驗證。首先我們測試sleep方法。

public class Test2 {
    private final static Object lock = new Object();
    public static void main(String[] args) {
        Stream.of("線程1", "線程2").forEach(n -> new Thread(n) {
            public void run() {
                Test2.testSleep();
            }
        }.start());
    }
    private static void testSleep() {
        try {
            Thread.sleep(10_000);
            System.out.println("休眠結束");
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

這個方法會依次運行,不會出現任何異常。然後我們主要是看wait方法。

public class Test2 {
    private final static Object lock = new Object();
    public static void main(String[] args) {
        Stream.of("線程1", "線程2").forEach(n -> new Thread(n) {
            public void run() {
                Test2.testSleep();
            }
        }.start());
    }
    private static void testWait() {
        try {
            lock.wait(10_000);
            System.out.println("wait結束");
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

我們運行一下,看一下結果:

v2-50b186c8eac137b7dc185140789bfc59_1440w.jpg

OK,下面我們驗證一下第四條區別:

四、sleep不需要被喚醒,wait需要


sleep方法很簡單,我們主要關注wait方法。看代碼:

首先我們定義兩個方法,一個等待方法,一個喚醒方法。

public class Test2 {
    private final static Object lock = new Object();
    private static void testWait() {
        synchronized (lock) {
            try {
                System.out.println("我一直在等待");
                lock.wait();
                System.out.println("wait被喚醒了");
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
    private static void notifyWait() {
        synchronized (lock) {
            try {
                Thread.sleep(5_000);
                lock.notify();
                System.out.println("休眠5秒鐘喚醒wait");
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

然後再去測試一下:

public class Test2 {
    private final static Object lock = new Object();
    public static void main(String[] args) {
        new Thread() {//這個線程一直在等待
            public void run() {
                Test2.testWait();
            }
        }.start();
        new Thread() {//這個線程準備去喚醒
            public void run() {
                Test2.notifyWait();
            }
        }.start();
    }
}

如果沒有喚醒方法,那第一個線程就會處於一直等待的狀態,第二個線程喚醒了之後就不再等待了。

v2-d51b34a7d0deff8f2f54464224062000_1440w.png

以上就是四個區別的完整驗證,如有問題還請指正。

Leave a Reply

Your email address will not be published. Required fields are marked *