1. 程式人生 > >Thread->sleep、wait、join使用

Thread->sleep、wait、join使用

Thread sleep、wait、join使用

這裡先介紹join,然後把兩個有關聯的sleep和wait一起介紹.

join()

這個方法比較好理解,當前執行緒等待指定執行緒終止後在執行,將兩個交替執行的執行緒合併為順序執行的執行緒.比如在B執行緒中呼叫A執行緒的join()方法,直到A執行緒執行完畢,B執行緒才會繼續執行.

api有兩個

  • void join()

    當前執行緒等待呼叫這個方法的執行緒終止後再執行.

  • void join(long millis)

    當前執行緒等待millis毫秒後,不管呼叫這個方法的執行緒是否終止,當前執行緒將變成可執行狀態

這裡我們不妨看一下join的原始碼,join()中呼叫的join(long millis)所以這裡我只貼出join(long millis)方法

可以發現他先呼叫isAlive()方法判斷該執行緒是否存活,然後通過Object.wait(0)方法使當前執行緒阻塞(客官別急wait後面會講到).

通過註釋可以發現執行緒存活指的是執行緒存活並且呼叫start()了.接下來我們通過一個例子體會下join的使用

public class MyClass {

    public static void main(String[] args) {

        System.out.println("main start");

        Thread t = new Thread(new MyRunnable());
        t.start();

        try
{ t.join(); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("main end"); } private static class MyRunnable implements Runnable { @Override public void run() { System.out.println("other start"
); try { Thread.sleep(3000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("other end"); } } }

執行結果.

先列印了main start,然後由於我們呼叫了t.join()於是主執行緒等待other執行緒執行完畢後在繼續執行,所以打印出other start,other end,最後打印出main end.

接下來我們將join()方法替換成join(1000)

執行結果.

這裡應該沒啥問題就不多說了.

sleep()

Thread靜態方法,強制當前執行緒休眠.當休眠時間到期後恢復到可執行狀態.不一定會立即執行,具體取決於執行緒排程器.

下面一個很簡單的栗子

public static void main(String[] args) {

    System.out.println("main start");

    try {
        Thread.sleep(3000);
    } catch (InterruptedException e) {
        e.printStackTrace();
    }

    System.out.println("main end");

}

結果就是先列印main start,然後休眠3秒後列印,main end.

我主要想說的是下面這個問題,sleep()在Synchronized塊中呼叫,執行緒雖然休眠了,但是物件的鎖並未釋放,其他執行緒無法訪問這個物件.下面一個例子展示這個過程.

public static void main(String[] args) {

        MyRunnable r = new MyRunnable();
        Thread t = new Thread(r);
        t.start();
        r.method1();
    }

    private static class MyRunnable implements Runnable {

        @Override
        public synchronized void run() {
            System.out.println("other start");

            System.out.println("other end");
        }

        public synchronized void  method1(){
            System.out.println("main start");

            try {
                Thread.sleep(3000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }

            System.out.println("main end");
        }
    }

結果如下

可以發現即使method1()中呼叫了Thread.sleep(),但物件鎖並未釋放,所以休眠3秒後依次執行main end->other start->other end.這裡可以留意下後面在wait()方法的時候我們會做個對比.

wait()

wait()一般都是配合notify()和notifyAll()一起使用,並且他們都是java.lang.Object的方法.

具體細節如下

  • wait()方法是使當前執行緒,進入到一個和物件相關的等待池中,同時失去物件的鎖,其他執行緒可以訪問.

  • wait()方法通過notify(),notifyAll(),或者指定超時時間來喚醒當前等待池中執行緒.

  • wait()必須用在synchronized程式碼塊中,否則會丟擲異常.

下面通過一個栗子檢驗下

public static void main(String[] args) {

    MyRunnable r = new MyRunnable();
    Thread t = new Thread(r);
    t.start();
    r.method1();
}

private static class MyRunnable implements Runnable {

    @Override
    public synchronized void run() {
        System.out.println("other start");

        System.out.println("other end");

        notifyAll();
    }

    public synchronized void method1() {
        System.out.println("main start");

        try {
            wait();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        System.out.println("main end");
    }
}

執行結果如下

由於執行緒建立需要時間,所以先執行了method1()方法打印出main start,然後呼叫wait()方法後該執行緒進入等待狀態,並且釋放該物件鎖,於是run()執行打印出other start和other end,然後呼叫notifyAll()後wait的執行緒被喚醒打印出main end.

這裡細說下notify()與notifyAll(),

  • notify()

    舉個例子如果執行緒A1,A2,A3都obj.wait(),則B呼叫obj.notify()只能喚醒A1,A2,A3中的一個(具體哪一個由JVM決定)

  • notifyAll()

    obj.notifyAll()則能全部喚醒A1,A2,A3,但是要繼續執行obj.wait()的下一條語句,必須獲得obj鎖,因此,A1,A2,A3只有一個有機會獲得鎖繼續執行,例如A1,其餘的需要等待A1釋放obj鎖之後才能繼續執行.

sleep()與wait()區別

  • sleep()

    1. sleep是Thread靜態方法,因此他不能改變物件的機鎖,所以當在一個Synchronized塊中呼叫Sleep()方法是,執行緒雖然休眠了,但是物件的鎖並木有被釋放,其他執行緒無法訪問這個物件(即使睡著也持有物件鎖).
  • wait()

    1. wait()是Object類裡的方法,當一個執行緒執行到wait()方法時,它就進入到一個和該物件相關的等待池中,同時失去了物件的鎖,其他執行緒可以訪問.

    2. wait()使用notify或者notifyAlll或者指定睡眠時間來喚醒當前等待池中的執行緒

    3. wait()必須放在synchronized block中,否則會在執行時丟擲”java.lang.IllegalMonitorStateException”異常.

一句話總結最大區別:sleep()睡眠時,保持物件鎖,仍然佔有該鎖;而wait()睡眠時,釋放物件鎖.

總結

  1. join將並行執行的執行緒改為序列.

  2. sleep使當前執行緒休眠,目的是不讓當前執行緒獨自霸佔該程序所獲的CPU資源,以留一定時間給其他執行緒執行的機會.

  3. 在某種條件下該執行緒wait(),當條件成熟後通過notify()喚醒該執行緒繼續執行.