1. 程式人生 > >Java常用面試題14 如何暫停執行緒Sleep和Wait 你能分清楚嗎?

Java常用面試題14 如何暫停執行緒Sleep和Wait 你能分清楚嗎?

問:

Thread類的sleep()方法和Object類wait()方法都可以讓執行緒暫停執行,它們有什麼區別? 

答:

sleep()方法(休眠)是執行緒類(Thread)的靜態方法,呼叫此方法會讓當前執行緒暫停執行指定的時間,將執行機會(CPU)讓給其他執行緒,但是物件的鎖依然保持,因此休眠時間結束後會自動恢復。

public class Thread implements Runnable {

    public static native void sleep(long millis) throws InterruptedException;

    public static void sleep(long millis, int nanos)
    throws InterruptedException {
        if (millis < 0) {
            throw new IllegalArgumentException("timeout value is negative");
        }


        if (nanos < 0 || nanos > 999999) {
            throw new IllegalArgumentException(
                                "nanosecond timeout value out of range");
        }


        if (nanos >= 500000 || (nanos != 0 && millis == 0)) {
            millis++;
        }


        sleep(millis);
    }

}

wait()是Object類的方法,呼叫物件的wait()方法導致當前執行緒放棄物件的鎖(執行緒暫停執行),進入物件的等待池(wait pool),只有呼叫物件的notify()方法(或notifyAll()方法)時才能喚醒等待池中的執行緒進入等鎖池(lock pool),如果執行緒重新獲得物件的鎖就可以進入就緒狀態。

public class Object {

    public final void wait() throws InterruptedException {
        wait(0);
    }

   public final native void wait(long timeout) throws InterruptedException;

   public final void wait(long timeout, int nanos) throws InterruptedException {
        if (timeout < 0) {
            throw new IllegalArgumentException("timeout value is negative");
        }


        if (nanos < 0 || nanos > 999999) {
            throw new IllegalArgumentException(
                                "nanosecond timeout value out of range");
        }


        if (nanos > 0) {
            timeout++;
        }




        wait(timeout);
    }

    public final native void notify();

    public final native void notifyAll();

}

拓展:

什麼是執行緒?什麼是程序?

    程序是具有一定獨立功能的程式關於某個資料集合上的一次執行活動,是作業系統進行資源分配和排程的一個獨立單位;

    執行緒是程序的一個實體,是CPU排程和分派的基本單位,是比程序更小的能獨立執行的基本單位。

    執行緒的劃分尺度小於程序,這使得多執行緒程式的併發性高;

    程序在執行時通常擁有獨立的記憶體單元,而執行緒之間可以共享記憶體。

    使用多執行緒的程式設計通常能夠帶來更好的效能和使用者體驗,但是多執行緒的程式對於其他程式是不友好的,因為它可能佔用了更多的CPU資源。

    當然,也不是執行緒越多,程式的效能就越好,因為執行緒之間的排程和切換也會浪費CPU時間。時下很時髦的Node.js就採用了單執行緒非同步I/O的工作模式。

拓展:

轉載自: http://dylanxu.iteye.com/blog/1322066

1、sleep()

使當前執行緒(即呼叫該方法的執行緒)暫停執行一段時間,讓其他執行緒有機會繼續執行,但它並不釋放物件鎖。也就是說如果有synchronized同步快,其他執行緒仍然不能訪問共享資料。注意該方法要捕捉異常。

例如有兩個執行緒同時執行(沒有synchronized)一個執行緒優先順序為MAX_PRIORITY,另一個為MIN_PRIORITY,如果沒有Sleep()方法,只有高優先順序的執行緒執行完畢後,低優先順序的執行緒才能夠執行;但是高優先順序的執行緒sleep(500)後,低優先順序就有機會執行了。

總之,sleep()可以使低優先順序的執行緒得到執行的機會,當然也可以讓同優先順序、高優先順序的執行緒有執行的機會。

2、join()

join()方法使呼叫該方法的執行緒在此之前執行完畢,也就是等待該方法的執行緒執行完畢後再往下繼續執行。注意該方法也需要捕捉異常。

3、yield()

該方法與sleep()類似,只是不能由使用者指定暫停多長時間,並且yield()方法只能讓同優先順序的執行緒有執行的機會。

4、wait()和notify()、notifyAll()

這三個方法用於協調多個執行緒對共享資料的存取,所以必須在synchronized語句塊內使用。synchronized關鍵字用於保護共享資料,阻止其他執行緒對共享資料的存取,但是這樣程式的流程就很不靈活了,如何才能在當前執行緒還沒退出synchronized資料塊時讓其他執行緒也有機會訪問共享資料呢?此時就用這三個方法來靈活控制。

wait()方法使當前執行緒暫停執行並釋放物件鎖標示,讓其他執行緒可以進入synchronized資料塊,當前執行緒被放入物件等待池中。當呼叫notify()方法後,將從物件的等待池中移走一個任意的執行緒並放到鎖標誌等待池中,只有鎖標誌等待池中執行緒能夠獲取鎖標誌;如果鎖標誌等待池中沒有執行緒,則notify()不起作用。

notifyAll()則從物件等待池中移走所有等待那個物件的執行緒並放到鎖標誌等待池中。

注意 這三個方法都是java.lang.Object的方法。

二、run和start()

把需要處理的程式碼放到run()方法中,start()方法啟動執行緒將自動呼叫run()方法,這個由java的記憶體機制規定的。並且run()方法必需是public訪問許可權,返回值型別為void。

三、關鍵字synchronized

該關鍵字用於保護共享資料,當然前提條件是要分清哪些資料是共享資料。每個物件都有一個鎖標誌,當一個執行緒訪問到該物件,被Synchronized修飾的資料將被"上鎖",阻止其他執行緒訪問。當前執行緒訪問完這部分資料後釋放鎖標誌,其他執行緒就可以訪問了。

四、wait()和notify(),notifyAll()是Object類的方法,sleep()和yield()是Thread類的方法。

(1)、常用的wait方法有wait()和wait(long timeout);

void wait() 在其他執行緒呼叫此物件的 notify() 方法或者 notifyAll()方法前,導致當前執行緒等待。

void wait(long timeout)在其他執行緒呼叫此物件的notify() 方法 或者 notifyAll()方法,或者超過指定的時間量前,導致當前執行緒等待。

wait()後,執行緒會釋放掉它所佔有的“鎖標誌”,從而使執行緒所在物件中的其他shnchronized資料可被別的執行緒使用。

wait()h和notify()因為會對物件的“鎖標誌”進行操作,所以他們必需在Synchronized函式或者 synchronized block 中進行呼叫。如果在non-synchronized 函式或 non-synchronized block 中進行呼叫,雖然能編譯通過,但在執行時會發生IllegalMonitorStateException的異常。。

(2)、Thread.sleep(long millis)必須帶有一個時間引數。

sleep(long)使當前執行緒進入停滯狀態,所以執行sleep()的執行緒在指定的時間內肯定不會被執行;

sleep(long)可使優先順序低的執行緒得到執行的機會,當然也可以讓同優先順序的執行緒有執行的機會;

sleep(long)是不會釋放鎖標誌的。

(3)、yield()沒有引數

sleep 方法使當前執行中的執行緒睡眠一段時間,進入不可以執行狀態,這段時間的長短是由程式設定的,yield方法使當前執行緒讓出CPU佔有權,但讓出的時間是不可設定的。

yield()也不會釋放鎖標誌。

實際上,yield()方法對應瞭如下操作;先檢測當前是否有相同優先順序的執行緒處於同可執行狀態,如有,則把CPU的佔有權交給次執行緒,否則繼續執行原來的執行緒,所以yield()方法稱為“退讓”,它把執行機會讓給了同等級的其他執行緒。

sleep 方法允許較低優先順序的執行緒獲得執行機會,但yield()方法執行時,當前執行緒仍處在可執行狀態,所以不可能讓出較低優先順序的執行緒此時獲取CPU佔有權。在一個執行系統中,如果較高優先順序的執行緒沒有呼叫sleep方法,也沒有受到I/O阻塞,那麼較低優先順序執行緒只能等待所有較高優先順序的執行緒執行結束,方可有機會執行。

yield()只是使當前執行緒重新回到可執行狀態,所有執行yield()的執行緒有可能在進入到可執行狀態後馬上又被執行,所以yield()方法只能使同優先順序的執行緒有執行的機會。