線程的生命週期

  在最初談線程時,就提到過線程的生命週期要經過新建、就緒、運行、阻塞、死亡五種狀態。在線程被創建並啓動以後,它並不是一啓動就進入了執行狀態,也不是一直處於執行狀態。而且,在線程啓動以後,它不能一直“霸佔”CPU“獨自運行,所以CPU需要在多條線程之間切換,於是線程也會多次在運行、阻塞之間切換。線程的各種狀態之間的關係可以用一個圖來表示:

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

      就緒狀態:當線程對象調用了start()方法之後,該線程就處於就緒狀態,java虛擬機會爲其創建方法覅用棧和程序計數器,處於這個狀態中的線程並沒有開始運行,它只是表示該線程可以運行了。至於該線程何時開始運行,取決於JVM的線程裏調度器的調度。

  我們可以來看以下下面這段代碼:

   


  運行結果:



  我們可以看出,當主線程在i=2時調用了子線程的start方法來啓動當前線程,但當前線程並沒有立即執行,而是等到主線程爲22時纔看到子線程開始執行。如果程序希望調用子線程的start方法之後子線程立即開始執行,程序可以使用Thread.sleep(1)來讓當前運行的線程(主線程)睡眠1毫秒,它就會去執行另外一條就緒的子線程,這樣,我們的子線程就可以執行了。

        另外,啓動線程的是start方法,而不是run方法!永遠不要用run方法來啓動線程,系統會把該run方法當成線程執行體來處理。但如果直接調用run方法,則run方法就你立即被執行,而且在run方法返回之前其他線程無法併發執行,也就是說,系統把線程對象當成一個普通對象,而run方法也是一個普通方法,而不是執行體。比如,我們把上面那段代碼中的thread.start()改爲”thread.run();以後,程序運行的結果是整個程序只有一條線程:主線程。而且,run方法裏不能用getName方法來獲取線程名。

        還有,我們要特別注意不要對已經啓動的線程再次調用start()方法,這樣會引發IllegalThreadStateException()異常。

       運行狀態:毫無疑問,處於就緒狀態的線程獲取了CPU,開始執行run方法的線程執行體,那麼該線程就處於運行狀態。

       對於只有一個cpu的計算機來說,在任何時刻只有一條線程處於運行狀態,這也就是我們通常所說的“宏觀並行,微觀串行”。爲了合理的分配cpu的資源,線程在運行的時候有可能需要被中斷,目的是使其他線程獲得執行的機會,線程調度的細節取決於底層平臺所採用的策略。對於搶佔式策略的系統而言,系統會給每個可執行的線程一個小時間段來處理任務,當該時間段用完,系統就會剝奪該線程所佔用的資源,讓其他線程有執行的機會。在選擇下一個線程時,系統會考慮到線程的優先級。

       當發生如下情況,系統將會進入阻塞狀態:

               1.線程調用了sleep方法主動放棄所佔用的處理器資源。

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

               3.線程試圖獲取一個同步監聽器,但是該同步監聽器正被其他線程所佔用。

               4.線程正在等待某個通知(notify)

                5.程序調用了線程的suspend方法將該線程掛起。不過,這個方法容易導致死鎖,所以,程序應該儘量避免使用該方法。

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

        針對上面的幾種情況,當發生如下特定的情況可以將上面的阻塞接觸,讓該線程重新進入就緒狀態:

                 1、調用sleep方法的線程經過了指定的時間,時間單位是毫秒。

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

                  3.線程成功的獲取了試圖獲取的同步監聽器。

                  4.線程正在等待某個通知時,其他線程發出了一個通知。

                   5.處於掛起狀態的線程被調用了resume恢復方法。

  線程會以以下三種方式之一結束,結束之後就是死亡狀態:

    1.run()方法執行完畢,線程正常結束。

                 2.線程拋出一個未捕獲的異常或錯誤。

                3.直接調用該線程的stop方法來結束該線程,該方法容易導致死鎖,通常情況下不建議使用。

        注意:當主線程結束後,其他線程不受任何影響,並不會隨之結束。一旦子線程啓動起來就會擁有和主線程相同的地位,它不會受主線程的控制。

         爲了測試某個線程是否已經死亡,可以用isAlive()方法,當線程處於就緒、運行、阻塞三種狀態時,返回true,當線程處於死亡狀態時,返回false。不要試圖對已經死亡的線程再次調用start方法,死亡就是死亡,該線程將不能再次作爲線程執行。如:

      

 

發佈了40 篇原創文章 · 獲贊 2 · 訪問量 2萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章