線程狀態的轉換(java.lang.Thread.State 里面有 6 種線程狀態):
1、NEW 狀態
用 new Thread() 建立一個線程對象后,該線程對象就處於 NEW 狀態。
2、RUNNABLE 狀態
通過調用線程的 start() 進入 RUNNABLE 狀態。
在操作系統層面,對 RUNNABLE 狀態還有細分,按是否有獲得 CPU 的控制權分為:READY 和 RUNNING 狀態。
2.1 READY 狀態
處於 READY 狀態的線程已經具備了運行條件,但還沒有分配到CPU,處於線程就緒隊列(就緒池),等待系統為其分配CPU。
注:如果希望子線程調用 start() 方法后立即執行,可以使用Thread.sleep()方式使主線程睡眠一伙兒,轉去執行子線程。
2.2 RUNNING 狀態
線程處於運行狀態。這個狀態最為復雜,它也可和其他狀態進行轉化,轉化的示意圖如上圖。
注: 在運行狀態的線程調用 yield(),它就會讓出cpu資源,再次變為就緒狀態。
3. WAITING 狀態
線程進入等待狀態
4. TIMED_WAITING 狀態
超時等待狀態。
注:處於 TIMED_WAITING 狀態的線程,除了等待超時時間到后結束等待外,還可以通過調用相應的喚醒方法:notify(),notifyAll(),unpark(),來強制喚醒等待的線程。(測試通過)
5、BLOCKED 狀態
當線程在等待獲取對象監視器鎖,從而進入 synchronized 方法(或synchronized 塊)時,線程就處於 BLOCKED 狀態。(具體可以查看 Thread.State#BLOCKED 的注釋)
6、TEMINATED 狀態
當線程的 run() 執行完,或者被強制性地終止,例如出現異常,或者調用了stop()、desyory()方法等等,就會從運行狀態轉變為死亡狀態。
線程一旦死亡,就不能復生。如果在一個死去的線程上調用start()方法,會拋出java.lang.IllegalThreadStateException異常。
Note:
守護線程
守護線程與普通線程寫法上基本么啥區別,調用線程對象的方法setDaemon(true),則可以將其設置為守護線程。
setDaemon方法的詳細說明:
public final void setDaemon(boolean on)將該線程標記為守護線程或用戶線程。當正在運行的線程都是守護線程時,Java 虛擬機退出。該方法必須在啟動線程前調用。
實際上:JRE判斷程序是否執行結束的標准是所有的前台執線程行完畢了,而不管后台線程的狀態,因此,在使用后台線程(守護線程)時候一定要注意這個問題。
守護線的好處就是你不需要關心它的結束問題。例如你在你的應用程序運行的時候希望播放背景音樂,如果將這個播放背景音樂的線程設定為非守護線程,那么在用戶請求退出的時候,
不僅要退出主線程,還要通知播放背景音樂的線程退出;如果設定為守護線程則不需要了。
如何結束一個線程
Thread.stop()、Thread.suspend、Thread.resume、Runtime.runFinalizersOnExit這些終止線程運行的方法已經被廢棄了,使用它們是極端不安全的!
想要安全有效的結束一個線程,只要保證在一定的情況下,run方法能夠執行完畢即可。而不是while(true)的無線循環。
有些時候線程的run()方法不能正常的執行完畢(可能在運行時轉成了阻塞),這種情況下可以借助Thread對象的interrupt()方法將中斷狀態設置為true來結束一個線程。
當一個線程處於sleep、wait、join這三種狀態之一的時候,如果此時他的中斷狀態為true,那么它就會拋出一個InterruptedException的異常,並將中斷狀態重新設置為false。
我們看看sleep、wait、join方法的聲明:
public final void wait() throws InterruptedException public static native void sleep(long millis) throws InterruptedException public final void join() throws InterruptedException
可以看到,這三者有一個共同點,都拋出了一個InterruptedException的異常。所以,我們可以使用interrupt這個巧妙的方式結束掉這種情況下的線程。