1. 程式人生 > >五、線程的生命周期

五、線程的生命周期

rup 之前 lin println exec 暫時 tin 否則 plain

線程是一個動態執行的過程,它也有從創建到死亡的過程。

在 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 方法被調用之後,線程對象處於阻塞狀態。寫在哪個線程中,哪個線程阻塞

    這種也稱為聯合線程,就是說把當前線程和當前線程所在的線程聯合成一個線程

    

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 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(); } } } } }

  結果:

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 玩遊戲0 玩遊戲1 玩遊戲2 玩遊戲3 玩遊戲4 玩遊戲5 播放音樂0 播放音樂1 播放音樂2 播放音樂3 播放音樂4 播放音樂5 播放音樂6 播放音樂7 播放音樂8 播放音樂9 玩遊戲6 玩遊戲7 玩遊戲8 玩遊戲9

  

後臺線程(守護線程):在後臺運行的線程,其目的是為其他線程提供服務,也稱為“守護線程”。

①、JVM 的垃圾回收線程就是守護線程。

②、main 方法是前臺線程,不是後臺線程

  技術分享圖片

1 2 3 4 5 6 7 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 方法後,線程進入就緒狀態。

五、線程的生命周期