Java多線程學習(三)---線程的生命週期

線程生命週期

摘要服務器

當線程被建立並啓動之後,它既不是一啓動就進入了執行狀態,也不是一直處於執行狀態。在線程的生命週期中,它要通過新建(New)、就緒Runnable)、運行Running)、阻塞(Blocked)和死亡(Dead)5種狀態。尤爲是當線程啓動之後,它不可能一直"霸佔"着CPU獨自運行,因此CPU須要在多條線程之間切換,因而線程狀態也會屢次在運行、阻塞之間切換併發

1. 新建狀態,當程序使用new關鍵字建立了一個線程以後,該線程就處於新建狀態,此時僅由JVM爲其分配內存,並初始化其成員變量的值測試

2. 就緒狀態,當線程對象調用了start()方法以後,該線程處於就緒狀態。Java虛擬機會爲其建立方法調用棧和程序計數器,等待調度運行spa

3. 運行狀態,若是處於就緒狀態的線程得到了CPU,開始執行run()方法的線程執行體,則該線程處於運行狀態操作系統

4. 阻塞狀態,當處於運行狀態的線程失去所佔用資源以後,便進入阻塞狀態線程

5. 在線程的生命週期當中,線程的各類狀態的轉換過程設計

1、新建和就緒狀態

當程序使用new關鍵字建立了一個線程以後,該線程就處於新建狀態,此時它和其餘的Java對象同樣,僅僅由Java虛擬機爲其分配內存,並初始化其成員變量的值。此時的線程對象沒有表現出任何線程的動態特徵,程序也不會執行線程的線程執行體。對象

當線程對象調用了start()方法以後,該線程處於就緒狀態。Java虛擬機會爲其建立方法調用棧程序計數器,處於這個狀態中的線程並無開始運行,只是表示該線程能夠運行了。至於該線程什麼時候開始運行,取決於JVM裏線程調度器的調度。blog

注意:啓動線程使用start()方法,而不是run()方法。永遠不要調用線程對象的run()方法。調用start0方法來啓動線程,系統會把該run()方法當成線程執行體來處理;但若是直按調用線程對象的run()方法,則run()方法當即就會被執行,並且在run()方法返回以前其餘線程沒法併發執行。也就是說,系統把線程對象當成一個普通對象,而run()方法也是一個普通方法,而不是線程執行體。須要指出的是,調用了線程的run()方法以後,該線程已經再也不處於新建狀態,不要再次調用線程對象的start()方法。只能對處於新建狀態的線程調用start()方法,不然將引起IllegaIThreadStateExccption異常。

調用線程對象的start()方法以後,該線程當即進入就緒狀態——就緒狀態至關於"等待執行",但該線程並未真正進入運行狀態。若是但願調用子線程的start()方法後子線程當即開始執行,程序可使用Thread.sleep(1) 來讓當前運行的線程(主線程)睡眠1毫秒,1毫秒就夠了,由於在這1毫秒內CPU不會空閒,它會去執行另外一個處於就緒狀態的線程,這樣就可讓子線程當即開始執行。生命週期

2、運行和阻塞狀態

2.1 線程調度

若是處於就緒狀態的線程得到了CPU,開始執行run()方法的線程執行體,則該線程處於運行狀態,若是計算機只有一個CPU。那麼在任什麼時候刻只有一個線程處於運行狀態,固然在一個多處理器的機器上,將會有多個線程並行執行;當線程數大於處理器數時,依然會存在多個線程在同一個CPU上輪換的現象。

當一個線程開始運行後,它不可能一直處於運行狀態(除非它的線程執行體足夠短,瞬間就執行結束了)。線程在運行過程當中須要被中斷,目的是使其餘線程得到執行的機會,線程調度的細節取決於底層平臺所採用的策略。對於採用搶佔式策略的系統而言,系統會給每一個可執行的線程一個小時間段來處理任務;當該時間段用完後,系統就會剝奪該線程所佔用的資源,讓其餘線程得到執行的機會。在選擇下一個線程時,系統會考慮線程的優先級

全部現代的桌面和服務器操做系統都採用搶佔式調度策略,但一些小型設備如手機則可能採用協做式調度策略,在這樣的系統中,只有當一個線程調用了它的sleep()或yield()方法後纔會放棄所佔用的資源——也就是必須由該線程主動放棄所佔用的資源。

2.2 線程阻塞

當發生以下狀況時,線程將會進入阻塞狀態

線程調用sleep()方法主動放棄所佔用的處理器資源

線程調用了一個阻塞式IO方法,在該方法返回以前,該線程被阻塞

線程試圖得到一個同步監視器,但該同步監視器正被其餘線程所持有。關於同步監視器的知識、後面將存更深刻的介紹

線程在等待某個通知notify

程序調用了線程的suspend()方法將該線程掛起。但這個方法容易致使死鎖,因此應該儘可能避免使用該方法

當前正在執行的線程被阻塞以後,其餘線程就能夠得到執行的機會。被阻塞的線程會在合適的時候從新進入就緒狀態,注意是就緒狀態而不是運行狀態。也就是說,被阻塞線程的阻塞解除後,必須從新等待線程調度器再次調度它。

2.3 解除阻塞

針對上面幾種狀況,當發生以下特定的狀況時能夠解除上面的阻塞,讓該線程從新進入就緒狀態

調用sleep()方法的線程通過了指定時間。

線程調用的阻塞式IO方法已經返回。

線程成功地得到了試圖取得的同步監視器。

線程正在等待某個通知時,其餘線程發出了個通知。

處於掛起狀態的線程被調甩了resdme()恢復方法。

圖 2.1 線程狀態轉換圖

從圖2.1中能夠看出,線程從阻塞狀態只能進入就緒狀態,沒法直接進入運行狀態。而就緒和運行狀態之間的轉換一般不受程序控制,而是由系統線程調度所決定。當處於就緒狀態的線程得到處理器資源時,該線程進入運行狀態;當處於運行狀態的線程失去處理器資源時,該線程進入就緒狀態。但有一個方法例外,調用yield()方法可讓運行狀態的線程轉入就緒狀態。關於yield()方法後面有更詳細的介紐。

3、線程死亡

3.1 死亡狀態

線程會以以下3種方式結束,結束後就處於死亡狀態

run()call()方法執行完成,線程正常結束。

線程拋出一個未捕獲的ExceptionError

直接調用該線程stop()方法來結束該線程——該方法容易致使死鎖,一般不推薦使用。

3.2 程序設計

主線程結束時,其餘線程不受任何影響,並不會隨之結束。一旦子線程啓動起來後,它就擁有和主線程相同的地位,它不會受主線程的影響。爲了測試某個線程是否已經死亡,能夠調用線程對象的isAlivc()方法,當線程處於就緒運行阻塞了種狀態時,該方法將返回true;當線程處於新建死亡狀態時,該方法將返回false

不要試圖對一個已經死亡的線程調用start()方法使它從新啓動,死亡就是死亡,該線程將不可再次做爲線程執行。

下面程序嘗試對處於死亡狀態的線程再次調用start()。

  1. package test;
  2. public class StartDead extends Thread {
  3.    private int i;
  4.  
  5.    // 重寫 run方法,run方法的方法體就是線程執行體
  6.    public void run() {
  7.       for (; i < 100; i++) {
  8.          System.out.println(getName() + "" + i);
  9.       }
  10.    }
  11.  
  12.    public static void main(String[] args) {
  13.       // 建立線程對象
  14.       StartDead sd = new StartDead();
  15.       for (int i = 0; i < 300; i++) {
  16.          // 調用 ThreadcurrentThread方法獲取當前線程
  17.          System.out.println(Thread.currentThread().getName() + "" + i);
  18.          if (i == 20) {
  19.             // 啓動線程
  20.             sd.start();
  21.             // 判斷啓動後線程的 isAlive()值,輸出true
  22.             System.out.println(sd.isAlive());
  23.          }
  24.          // 只有當線程處於新建、死亡兩種狀態時 isAlive()方法返回false
  25.          // i > 20,則該線程確定已經啓動過了,若是sd.isAlive()爲假時,
  26.          // 那隻能是死亡狀態了。
  27.          if (i > 20 && !sd.isAlive())
  28.  
  29.          {
  30.             // 試圖再次啓動該線程
  31.             sd.start();
  32.          }
  33.       }
  34.    }
  35. }

3.3 運行結果

main 0

main 1

main 2

main 3

main 4

main 5

main 6

main 7

main 8

main 9

main 10

main 11

main 12

main 13

main 14

main 15

main 16

main 17

main 18

main 19

main 20

true

main 21

…………

Thread-0 0

Thread-0 1

Thread-0 2

Thread-0 3

Thread-0 4

Thread-0 5

Thread-0 6

Thread-0 7

Thread-0 8

………………

Thread-0 92

Thread-0 93

Thread-0 94

Thread-0 95

Thread-0 96

Thread-0 97

Thread-0 98

Thread-0 99

main 25

Exception

 

main→主線程

Thread-0→線程1

Exception→異常

上面程序中的粗體字代碼試圖在線程已死亡的狀況下再次調用start()方法來啓動該線程。運行上面程序,將引起IllegaIThreadStateException異常,這代表處於死亡狀態的線程沒法再次運行了。

若是,您認爲閱讀這篇博客讓您有些收穫,不妨點擊一下右下角的【推薦】。
若是,您但願更容易地發現個人新博客,不妨點擊一下左下角的【關注我】。
若是,您對個人博客所講述的內容有興趣,請繼續關注個人後續博客,我是【Sunddenly】。

本文版權歸做者和博客園共有,歡迎轉載,但未經做者贊成必須保留此段聲明,且在文章頁面明顯位置給出原文鏈接,不然保留追究法律責任的權利

相關文章
相關標籤/搜索