天天看點

java多線程中wait()和notify()和notifyAll()及其他常用方法使用

一、首先說下線程的4種狀态,任何一種線程都處于這4種狀态之一。

1、産生(new):線程對象已經産生但是尚未被啟動,是以無法執行,通過new産生對象後沒有對它調用start()方法。

2、 可執行(Runnable):每一個支援多線程的系統都有一個排程器,排程器會從線程池中選擇一個線程并且啟動它,當一個線程處于可執行狀态的時候時候, 表示它可能處于線程池中等待排程器啟動它;也有可能它正在執行。如執行了一個線程對象的start()方法後,線程就處于可執行狀态。但是,顯然線程可能 并不是在執行中。

3、 停滞(Blocks):當一個線程處于停滞狀态的時候,系統排程器就會忽略它,不對它進行排程。當處于停滞狀态的線程重新回到可執行狀态的時候,它可能重 新執行。如通過對一個線程調用wait()方法後,線程就進入了停滞狀态。隻有對此調用notify()或者notifyAll()可使其回到可執行狀 态。

4 、死亡(Dead):當一個線程正常結束,它處于停滞狀态,如一個線程的run()方法執行完畢後。

二、線程中常用方法的使用介紹

1 、wait()之後notify()和notifyAll()的差別:

     wait 是Object 類的方法,對此對象調用wait 方法導緻本線程放棄對象鎖,進入等待此對象的等待鎖定池,隻有針對此對象發出notify 方法(或notifyAll)後本線程才進入對象鎖定池準備獲得對象鎖進入運作狀态。 wait() 和notify()因為會對對象的“鎖标志”進行操作,是以它們必須在 synchronized方法或synchronized block中進行調用。如果在non-synchronized方法或non- synchronized block中進行調用,雖然能編譯通過,但在運作時會發生IllegalMonitorStateException的異常。

這是一個wait()和notify()的例子:

線程A

synchronized(obj) {

while(!condition) {

obj.wait();

}

obj.doSomething();

}

當線程A獲得了obj鎖後,發現條件condition不滿足,無法繼續下一處理,于是線程A就wait()。

在另一線程B中,如果B更改了某些條件,使得線程A的condition條件滿足了,就可以喚醒線程A:

線程B

synchronized(obj) {

condition = true;

obj.notify();

}

      notify()和notifyAll()都是Object對象用于通知處在等待該對象的線程的方法,當一個線程進入wait之後,就必須等其他線程notify/notifyall,使用notifyall,可以喚醒所 有處于wait狀态的線程,使其重新進入鎖的争奪隊列中,而notify隻能喚醒一個。注意,任何時候隻有一個線程可以獲得鎖,也就是說隻有一個線程可以 運作synchronized中的代碼,notifyall隻是讓處于wait的線程重新擁有鎖的争奪權,但是隻會有一個獲得鎖并執行。

補充說明下,notify() notifiAll()隻會讓線程從阻塞狀态變成可執行狀态,注意是 可運作,并不是說立即馬上就會執行,對于處于可運作狀态的多個線程來說,

由JVM根據目前線程池當中的可運作狀态的線程的優先級來決定,即從runnable 到 run狀态變化是由JVM來完成。

2、線程中其他常用方法的差別

(a)  sleep():使目前線程進入停滞狀态,是以執行sleep()的線程在指定的時間内肯定不會執行;目前正在被服務的線程需要睡一會,醒來後繼續被服務。由 于sleep()方法是Thread類的方法,是以它不能改變對象的機鎖。是以當在一個Synchronized方法中調用sleep()時,線程雖然休 眠了,但是對象的機鎖沒有被釋放,其他線程仍然無法通路這個對象。sleep()方法不需要在同步的代碼塊中執行。但是sleep()可以通過 interrupt()方法打斷線程的暫停狀态,進而使線程立刻抛出InterruptedException。

(b) yield():目前正在被服務的線程可能覺得cpu的服務品質不夠好,于是提前退出,隻是使目前線程重新回到可執行狀态,是以執行yield()的線程有可能在進入到可執行狀态後馬上又被執行。 

sleep()可使優先級低的線程得到執行的機會,當然也可以讓同優先級和高優先級的線程有執行的機會;yield()隻能使同優先級的線程有執行的機會。 

(c) join(): 方法使目前線程停下來等待,直至另一個調用join方法的線程終止。值得注意的是,線程的在被start之後不一定馬上就運作,而是進入到可運作線程的隊 列中,通過join可以運作。但是join()可以通過interrupt()方法打斷線程的暫停狀态,進而使線程立刻抛出InterruptedException。舉例說明下: 當調用線程執行個體的start方法後,這個方法會立即傳回,如果在調用start方法後後需要使用一個由這個線程計算得到的值,就必須使用join方法。如果不使用join方法,就不能保證當執行到start方法後面的某條語句時,這 個線程一定會執行完。而使用join方法後,直到這個線程退出,程式才會往下執行

(d)  interrupt() 

interrupt() 中斷線程。需要注意的是,InterruptedException是線程自己從内部抛出的,并不是interrupt()方法抛出的。對某一線程調用 interrupt()時,如果該線程正在執行普通的代碼,那麼該線程根本就不會抛出InterruptedException。但是,一旦該線程進入到 wait()/sleep()/join()後,就會立刻抛出InterruptedException。

繼續閱讀