這篇博客介紹線程的生命周期。
線程是一個動態執行的過程,它也有從創建到死亡的過程。
在 Thread 類中,有一個枚舉內部類:
上面的信息以圖片表示如下:
第一張圖:
第二張圖:把等待、計時等待、阻塞看成阻塞一個狀態了
1、新建狀態(new):使用 new 創建一個線程,僅僅只是在堆中分配了內存空間
新建狀態下,線程還沒有調用 start()方法啟動,只是存在一個線程對象而已
Thread t = new Thread();//這就是t線程的新建狀態
2、可運行狀態(runnable):新建狀態調用 start() 方法,進入可運行狀態。而這個又分成兩種狀態,ready 和 running,分別表示就緒狀態和運行狀態
就緒狀態:線程對象調用了 start() 方法,等待 JVM 的調度,(此時該線程並沒有運行)
運行狀態:線程對象獲得 JVM 調度,如果存在多個 CPU,那么運行多個線程並行運行
注意:線程對象只能調用一次 start() 方法,否則報錯:illegaThreadStateExecptiong
3、阻塞狀態(blocked):正在運行的線程因為某種原因放棄 CPU,暫時停止運行,就會進入阻塞狀態。此時 JVM 不會給線程分配 CPU,知道線程重新進入就緒狀態,才有機會轉到 運行狀態。
注意:阻塞狀態只能先進入就緒狀態,不能直接進入運行狀態
阻塞狀態分為兩種情況:
①、當線程 A 處於可運行狀態中,試圖獲取同步鎖時,卻被 B 線程獲取,此時 JVM 把當前 A 線程放入鎖池中,A線程進入阻塞狀態
②、當線程處於運行狀態時,發出了 IO 請求,此時進入阻塞狀態
4、等待狀態(waiting):等待狀態只能被其他線程喚醒,此時使用的是無參數的 wait() 方法
①、當線程處於運行狀態時,調用了 wait() 方法,此時 JVM 把該線程放入等待池中
5、計時等待(timed waiting):調用了帶參數的 wait(long time)或 sleep(long time) 方法
①、當線程處於運行狀態時,調用了帶參數 wait 方法,此時 JVM 把該線程放入等待池中
②、當前線程調用了 sleep(long time) 方法
6、終止狀態(terminated):通常稱為死亡狀態,表示線程終止
①、正常終止,執行完 run() 方法,正常結束
②、強制終止,如調用 stop() 方法或 destory() 方法
③、異常終止,執行過程中發生異常
下面詳細介紹線程的幾種方法:
1、sleep(long millis)線程休眠:讓執行的線程暫停一段時間,進入計時等待狀態。
static void sleep(long millis):調用此方法后,當前線程放棄 CPU 資源,在指定的時間內,sleep 所在的線程不會獲得可運行的機會,此狀態下的線程不會釋放同步鎖(注意和 wait() 的區別,wait 會放棄 CPU 資源,同時也會放棄 同步鎖)
該方法更多的是用來模擬網絡延遲,讓多線程並發訪問同一資源時的錯誤效果更加明顯。
2、join()聯合線程:表示這個線程等待另一個線程完成后(死亡)才執行,join 方法被調用之后,線程對象處於阻塞狀態。寫在哪個線程中,哪個線程阻塞
這種也稱為聯合線程,就是說把當前線程和當前線程所在的線程聯合成一個線程
package com.ys.thread; class Join extends Thread{ @Override public void run() { for(int i = 0 ; i < 10 ;i++){ System.out.println("播放音樂"+i); } } } public class ThreadTest { public static void main(String[] args) { //創建 join 線程對象 Join joinThread = new Join(); for(int i = 0 ; i < 10 ; i++){ System.out.println("玩游戲"+i); if(i==3){ joinThread.start(); } if(i==5){ try { joinThread.join();//強制運行 join 線程,知道 join 運行完畢了,main 才有機會運行 } catch (InterruptedException e) { e.printStackTrace(); } } } } }
結果:
玩游戲0 玩游戲1 玩游戲2 玩游戲3 玩游戲4 玩游戲5 播放音樂0 播放音樂1 播放音樂2 播放音樂3 播放音樂4 播放音樂5 播放音樂6 播放音樂7 播放音樂8 播放音樂9 玩游戲6 玩游戲7 玩游戲8 玩游戲9
后台線程(守護線程):在后台運行的線程,其目的是為其他線程提供服務,也稱為“守護線程”。
①、JVM 的垃圾回收線程就是守護線程。
②、main 方法是前台線程,不是后台線程
public static void main(String[] args) { String mainThreadName = Thread.currentThread().getName(); System.out.println(mainThreadName); //main System.out.println(Thread.currentThread().isDaemon());//false }
特點:
①、若所有的前台線程都死亡,則后台線程自動死亡;
②、前台線程沒有結束,后台線程是不會結束的;
③、前台線程創建的線程是前台線程,后台線程創建的線程是后台線程。
Thread.setDaemon(Boolean on)必須在 start() 的方法前調用。否則會報錯。
線程的優先級:
每個線程都有一個優先級,這有助於 系統確定線程的調動順序。
Java 線程的優先級是一個整數,取值范圍是:1 (Thread.MIN_PRIORITY ) - 10 (Thread.MAX_PRIORITY )
默認情況下,每一個線程都會分配一個優先級 NORM_PRIORITY(5)。
具有較高優先級的線程對程序更重要,並且應該在低優先級的線程之前分配處理器資源。但是,線程優先級不能保證線程執行的順序,而且非常依賴於平台。
線程禮讓:
yield()方法:表示當前線程對象提示調度器自己願意讓出 CPU 資源,但是調度器可以自由的忽略該提示。
調用該方法后,線程對象進入就緒狀態,所以完全有可能:某個線程調用了 yield() 方法,但是線程調度器又把它調度出來重新執行。
從 Java7 提供的文檔上可以清楚的看出,開發中會很少使用該方法,該方法主要運用於調試或測試,它可能有助於多線程競爭條件下的錯誤重現現象。
sleep() 和 yield() 方法的區別:
①、都能使當前處於運行狀態的線程放棄 CPU資源,把運行的機會給其他線程
②、sleep 方法會給其他線程運行的機會,但是不考慮其他線程優先級的問題;yield 方法會優先給更高優先級的線程運行機會
③、調用 sleep 方法后,線程進入計時等待狀態,調用 yield 方法后,線程進入就緒狀態。