Java並發——線程間協作(wait、notify、sleep、yield、join)
1 線程的狀態
Java中線程中狀態可分為五種:New(新建狀態),Runnable(就緒狀態),Running(運行狀態),Blocked(阻塞狀態),Dead(死亡狀態)。
- New:新建狀態,當線程創建完成時為新建狀態,即new Thread(...),還沒有調用start方法時,線程處於新建狀態
- Runnable:就緒狀態,當調用線程的的start方法後,線程進入就緒狀態,等待CPU資源。處於就緒狀態的線程由Java運行時係統的線程調度程序(*thread scheduler*)來調度
- Running:運行狀態,就緒狀態的線程獲取到CPU執行權以後進入運行狀態,開始執行run方法
- Blocked:阻塞狀態,線程沒有執行完,由於某種原因(如,I/O操作等)讓出CPU執行權,自身進入阻塞狀態
- Dead:死亡狀態,線程執行完成或者執行過程中出現異常,線程就會進入死亡狀態。
這五種狀態之間的轉換關係如下圖所示:
2 wait
JDK中一共提供了這三個版本的方法
- wait()方法的作用是將當前運行的線程掛起(即讓其進入阻塞狀態),直到notify或notifyAll方法來喚醒線程
- wait(long timeout),該方法與wait()方法類似,唯一的區別就是在指定時間內,如果沒有notify或notifAll方法的喚醒,也會自動喚醒
- wait(long timeout,long nanos),在於更精確的控製調度時間
wait方法的使用必須在同步的範圍內,否則就會拋出IllegalMonitorStateException異常,wait方法的作用就是阻塞當前線程等待notify/notifyAll方法的喚醒,或等待超時後自動喚醒。
3 notify/notifyAll
有了對wait方法原理的理解,notify方法和notifyAll方法就很容易理解了。既然wait方式是通過對象的monitor對象來實現的,所以隻要在同一對象上去調用notify/notifyAll方法,就可以喚醒對應對象monitor上等待的線程了。notify和notifyAll的區別在於前者隻能喚醒monitor上的一個線程,對其他線程沒有影響,而notifyAll則喚醒所有的線程。
4 sleep/yield/join
4.1 sleep
sleep方法的作用是讓當前線程暫停指定的時間(毫秒),sleep方法是最簡單的方法,在上述的例子中也用到過,比較容易理解。唯一需要注意的是其與wait方法的區別。最簡單的區別是,wait方法依賴於同步,而sleep方法可以直接調用。而更深層次的區別在於sleep方法隻是暫時讓出CPU的執行權,並不釋放鎖。而wait方法則需要釋放鎖。
sleep暫停期間一直持有monitor對象鎖,其他線程是不能進入的。而wait方法則不同,當調用wait方法後,當前線程會釋放持有的monitor對象鎖,因此,其他線程還可以進入到同步方法,線程被喚醒後,需要競爭鎖,獲取到鎖之後再繼續執行。
4.2 yield
yield方法的作用是暫停當前線程,以便其他線程有機會執行,不過不能指定暫停的時間,並且也不能保證當前線程馬上停止。yield方法隻是將Running狀態轉變為Runnable狀態。
4.3 join
join方法的作用是父線程等待子線程執行完成後再執行,換句話說就是將異步執行的線程合並為同步的線程。JDK中提供三個版本的join方法,其實現與wait方法類似,join()方法實際上執行的join(0),而join(long millis, int nanos)也與wait(long millis, int nanos)的實現方式一致,暫時對納秒的支持也是不完整的。
最後更新:2017-07-26 09:04:54