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