閱讀909 返回首頁    go 阿裏雲 go 技術社區[雲棲]


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

  上一篇:go  Java並發——volatile的原理
  下一篇:go  Java並發——Synchronized優化(輕量級鎖、偏向鎖)