當線程被建立並啓動之後,它既不是一啓動就進入了執行狀態,也不是一直處於執行狀態。在線程的生命週期中,它要通過新建(New)、就緒(Runnable)、運行(Running)、阻塞(Blocked)和死亡(Dead)5種狀態。尤爲是當線程啓動之後,它不可能一直"霸佔"着CPU獨自運行,因此CPU須要在多條線程之間切換,因而線程狀態也會屢次在運行、阻塞之間切換。java
當程序使用new關鍵字建立了一個線程以後,該線程就處於 新建狀態,此時的線程狀況以下:程序員
當線程對象調用了start()方法以後,該線程處於 就緒狀態。此時的線程狀況以下:數據庫
調用start()方法與run()方法,對好比下:安全
如何讓子線程調用start()方法以後當即執行而非"等待執行":
多線程
3 運行(Running)狀態
性能
當CPU開始調度處於 就緒狀態 的線程時,此時線程得到了CPU時間片才得以真正開始執行run()方法的線程執行體,則該線程處於 運行狀態。測試
處於運行狀態的線程最爲複雜,它不可能一直處於運行狀態(除非它的線程執行體足夠短,瞬間就執行結束了),線程在運行過程當中須要被中斷,目的是使其餘線程得到執行的機會,線程調度的細節取決於底層平臺所採用的策略。線程狀態可能會變爲阻塞狀態、就緒狀態和死亡狀態。好比:
線程
4 阻塞(Blocked)狀態3d
處於運行狀態的線程在某些狀況下,讓出CPU並暫時中止本身的運行,進入 阻塞狀態。orm
當發生以下狀況時,線程將會進入阻塞狀態:
阻塞狀態分類:
在阻塞狀態的線程只能進入就緒狀態,沒法直接進入運行狀態。而就緒和運行狀態之間的轉換一般不受程序控制,而是由系統線程調度所決定。當處於就緒狀態的線程得到處理器資源時,該線程進入運行狀態;當處於運行狀態的線程失去處理器資源時,該線程進入就緒狀態。
4.1 等待(WAITING)狀態
線程處於無限制等待狀態,等待一個特殊的事件來從新喚醒,如:
以上兩種一旦經過相關事件喚醒線程,線程就進入了就緒(RUNNABLE)狀態繼續運行。
4.2 時限等待(TIMED_WAITING)狀態
線程進入了一個時限等待狀態,如:
5 死亡(Dead)狀態
線程會以以下3種方式結束,結束後就處於死亡狀態:
處於死亡狀態的線程對象也許是活的,可是,它已經不是一個單獨執行的線程。線程一旦死亡,就不能復生。若是在一個死去的線程上調用start()方法,會拋出java.lang.IllegalThreadStateException異常。
因此,須要注意的是:
5.1 終止(TERMINATED)狀態
線程執行完畢後,進入終止(TERMINATED)狀態。
6 線程相關方法
6.1 線程就緒、運行和死亡狀態轉換
就緒狀態轉換爲運行狀態:此線程獲得CPU資源;
運行狀態轉換爲就緒狀態:此線程主動調用yield()方法或在運行過程當中失去CPU資源。
運行狀態轉換爲死亡狀態:此線程執行執行完畢或者發生了異常;
注意:
6.2 run & start
經過調用start啓動線程,線程執行時會執行run方法中的代碼。
6.3 sleep & yield
sleep():經過sleep(millis)使線程進入休眠一段時間,該方法在指定的時間內沒法被喚醒,同時也不會釋放對象鎖;
好比,咱們想要使主線程每休眠100毫秒,而後再打印出數字:
注意以下幾點問題:
sleep是靜態方法,最好不要用Thread的實例對象調用它,由於它睡眠的始終是當前正在運行的線程,而不是調用它的線程對象,它只對正在運行狀態的線程對象有效。看下面的例子:
Java線程調度是Java多線程的核心,只有良好的調度,才能充分發揮系統的性能,提升程序的執行效率。可是無論程序員怎麼編寫調度,只能最大限度的影響線程執行的次序,而不能作到精準控制。由於使用sleep方法以後,線程是進入阻塞狀態的,只有當睡眠的時間結束,纔會從新進入到就緒狀態,而就緒狀態進入到運行狀態,是由系統控制的,咱們不可能精準的去幹涉它,因此若是調用Thread.sleep(1000)使得線程睡眠1秒,可能結果會大於1秒。
看某一次的運行結果:能夠發現,線程0首先執行,而後線程1執行一次,又了執行一次。發現並非按照sleep的順序執行的。
yield():與sleep相似,也是Thread類提供的一個靜態的方法,它也可讓當前正在執行的線程暫停,讓出CPU資源給其餘的線程。可是和sleep()方法不一樣的是,它不會進入到阻塞狀態,而是進入到就緒狀態。yield()方法只是讓當前線程暫停一下,從新進入就緒線程池中,讓系統的線程調度器從新調度器從新調度一次,徹底可能出現這樣的狀況:當某個線程調用yield()方法以後,線程調度器又將其調度出來從新進入到運行狀態執行。
關於sleep()方法和yield()方的區別以下:
6.4 join
線程的合併的含義就是將幾個並行線程的線程合併爲一個單線程執行,應用場景是當一個線程必須等待另外一個線程執行完畢才能執行時,Thread類提供了join方法來完成這個功能,注意,它不是靜態方法。
join有3個重載的方法:
例子代碼,以下:
在JDK中join方法的源碼,以下:
join方法實現是經過調用wait方法實現。當main線程調用t.join時候,main線程會得到線程對象t的鎖(wait 意味着拿到該對象的鎖),調用該對象的wait(等待時間),直到該對象喚醒main線程,好比退出後。這就意味着main 線程調用t.join時,必須可以拿到線程t對象的鎖。
6.5 suspend & resume (已過期)
suspend-線程進入阻塞狀態,但不會釋放鎖。此方法已不推薦使用,由於同步時不會釋放鎖,會形成死鎖的問題。
resume-使線程從新進入可執行狀態。
爲何 Thread.suspend 和 Thread.resume 被廢棄了?
Thread.suspend 天生容易引發死鎖。若是目標線程掛起時在保護系統關鍵資源的監視器上持有鎖,那麼其餘線程在目標線程恢復以前都沒法訪問這個資源。若是要恢復目標線程的線程在調用 resume 以前試圖鎖定這個監視器,死鎖就發生了。這種死鎖通常自身表現爲「凍結( frozen )」進程。
6.6 stop(已過期)
不推薦使用,且之後可能去除,由於它不安全。爲何 Thread.stop 被廢棄了?
由於其天生是不安全的。中止一個線程會致使其解鎖其上被鎖定的全部監視器(監視器以在棧頂產生ThreadDeath異常的方式被解鎖)。若是以前被這些監視器保護的任何對象處於不一致狀態,其它線程看到的這些對象就會處於不一致狀態。這種對象被稱爲受損的 (damaged)。當線程在受損的對象上進行操做時,會致使任意行爲。這種行爲可能微妙且難以檢測,也可能會比較明顯。
不像其餘未受檢的(unchecked)異常, ThreadDeath 悄無聲息的殺死及其餘線程。所以,用戶得不到程序可能會崩潰的警告。崩潰會在真正破壞發生後的任意時刻顯現,甚至在數小時或數天以後。
6.7 wait & notify/notifyAll
wait & notify/notifyAll這三個都是Object類的方法。使用 wait ,notify 和 notifyAll前提是先得到調用對象的鎖。
前面一直提到兩個概念,等待隊列(等待池),同步隊列(鎖池),這二者是不同的。具體以下:
被notify或notifyAll喚起的線程是有規律的,具體以下:
6.8 線程優先級
每一個線程執行時都有一個優先級的屬性,優先級高的線程能夠得到較多的執行機會,而優先級低的線程則得到較少的執行機會。與線程休眠相似,線程的優先級仍然沒法保障線程的執行次序。只不過,優先級高的線程獲取CPU資源的機率較大,優先級低的也並不是沒機會執行。
Thread類提供了setPriority(int newPriority)和getPriority()方法來設置和返回一個指定線程的優先級,其中setPriority方法的參數是一個整數,範圍是1~10之間,也可使用Thread類提供的三個靜態常量:
例子代碼,以下:
從執行結果能夠看到 ,通常狀況下,高級線程更顯執行完畢。
注意一點:
6.9 守護線程
守護線程與普通線程寫法上基本沒啥區別,調用線程對象的方法setDaemon(true),則能夠將其設置爲守護線程。
守護線程使用的狀況較少,但並不是無用,舉例來講,JVM的垃圾回收、內存管理等線程都是守護線程。還有就是在作數據庫應用時候,使用的數據庫鏈接池,鏈接池自己也包含着不少後臺線程,監控鏈接個數、超時時間、狀態等等。
setDaemon方法詳細說明:
執行結果:
從上面的執行結果能夠看出:前臺線程是保證執行完畢的,後臺線程尚未執行完畢就退出了。
6.10 如何結束一個線程
Thread.stop()、Thread.suspend、Thread.resume、Runtime.runFinalizersOnExit這些終止線程運行的方法已經被廢棄了,使用它們是極端不安全的!想要安全有效的結束一個線程,可使用下面的方法。
好比run方法這樣寫:只要保證在必定的狀況下,run方法可以執行完畢便可。而不是while(true)的無限循環。
誠然,使用上面方法的標識符來結束一個線程,是一個不錯的方法,但其也有弊端,若是該線程是處於sleep、wait、join的狀態時候,while循環就不會執行,那麼咱們的標識符就無用武之地了,固然也不能再經過它來結束處於這3種狀態的線程了。
因此,此時可使用interrupt這個巧妙的方式結束掉這個線程。咱們先來看看sleep、wait、join方法的聲明:
能夠看到,這三者有一個共同點,都拋出了一個InterruptedException的異常。在何時會產生這樣一個異常呢?
看下面的簡單的例子:
測試結果:
能夠看到,首先執行第一次while循環,在第一次循環中,睡眠2秒,而後將中斷狀態設置爲true。當進入到第二次循環的時候,中斷狀態就是第一次設置的true,當它再次進入sleep的時候,立刻就拋出了InterruptedException異常,而後被咱們捕獲了。而後中斷狀態又被從新自動設置爲false了(從最後一條輸出能夠看出來)。
因此,咱們可使用interrupt方法結束一個線程。具體使用以下:
多測試幾回,會發現通常有兩種執行結果:
或者
這兩種結果偏偏說明了,只要一個線程的中斷狀態一旦爲true,只要它進入sleep等狀態,或者處於sleep狀態,立馬回拋出InterruptedException異常。