天天看點

線程的5種狀态(建立、就緒、運作、阻塞、死亡)

1. 建立(NEW):新建立了一個線程對象。

2. 可運作(RUNNABLE):線程對象建立後,其他線程(比如main線程)調用了該對象的start()方法。該狀态的線程位于可運作線程池中,等待被線程排程選中,擷取cpu 的使用權 。

3. 運作(RUNNING):可運作狀态(runnable)的線程獲得了cpu 時間片(timeslice) ,執行程式代碼。

4. 阻塞(BLOCKED):阻塞狀态是指線程因為某種原因放棄了cpu 使用權,也即讓出了cpu timeslice,暫時停止運作。直到線程進入可運作(runnable)狀态,才有機會再次獲得cpu timeslice 轉到運作(running)狀态。阻塞的情況分三種: 

(一). 等待阻塞:運作(running)的線程執行o.wait()方法,JVM會把該線程放入等待隊列(waitting queue)中。

(二). 同步阻塞:運作(running)的線程在擷取對象的同步鎖時,若該同步鎖被别的線程占用,則JVM會把該線程放入鎖池(lock pool)中。

(三). 其他阻塞:運作(running)的線程執行Thread.sleep(long ms)或t.join()方法,或者發出了I/O請求時,JVM會把該線程置為阻塞狀态。當sleep()狀态逾時、join()等待線程終止或者逾時、或者I/O處理完畢時,線程重新轉入可運作(runnable)狀态。

5. 死亡(DEAD):線程run()、main() 方法執行結束,或者因異常退出了run()方法,則該線程結束生命周期。死亡的線程不可再次複生。

一.線程的狀态圖

線程的5種狀态(建立、就緒、運作、阻塞、死亡)

二.初始狀态

  1. 實作Runnable接口和繼承Thread可以得到一個線程類,new一個執行個體出來,線程就進入了初始狀态

三.可運作狀态

  1. 可運作狀态隻是說你資格運作,排程程式沒有挑選到你,你就永遠是可運作狀态。
  2. 調用線程的start()方法,此線程進入可運作狀态。
  3. 目前線程sleep()方法結束,其他線程join()結束,等待使用者輸入完畢,某個線程拿到對象鎖,這些線程也将進入可運作狀态。
  4. 目前線程時間片用完了,調用目前線程的yield()方法,目前線程進入可運作狀态。
  5. 鎖池裡的線程拿到對象鎖後,進入可運作狀态。

四.運作狀态

  1. 線程排程程式從可運作池中選擇一個線程作為目前線程時線程所處的狀态。這也是線程進入運作狀态的唯一一種方式。

五.死亡狀态

  1. 當線程的run()方法完成時,或者主線程的main()方法完成時,我們就認為它死去。這個線程對象也許是活的,但是,它已經不是一個單獨執行的線程。線程一旦死亡,就不能複生。
  2. 在一個死去的線程上調用start()方法,會抛出java.lang.IllegalThreadStateException異常。

六.阻塞狀态

  1. 目前線程T調用Thread.sleep()方法,目前線程進入阻塞狀态。
  2. 運作在目前線程裡的其它線程t2調用join()方法,目前線程進入阻塞狀态。
  3. 等待使用者輸入的時候,目前線程進入阻塞狀态。

七.等待隊列(本是Object裡的方法,但影響了線程)

  1. 調用obj的wait(), notify()方法前,必須獲得obj鎖,也就是必須寫在synchronized(obj) 代碼段内。
  2. 與等待隊列相關的步驟和圖
  • 線程1擷取對象A的鎖,正在使用對象A。
  • 線程1調用對象A的wait()方法。
  • 線程1釋放對象A的鎖,并馬上進入等待隊列。
  • 鎖池裡面的對象争搶對象A的鎖。
  • 線程5獲得對象A的鎖,進入synchronized塊,使用對象A。
  • 線程5調用對象A的notifyAll()方法,喚醒所有線程,所有線程進入鎖池。||||| 線程5調用對象A的notify()方法,喚醒一個線程,不知道會喚醒誰,被喚醒的那個線程進入鎖池。
  • notifyAll()方法所在synchronized結束,線程5釋放對象A的鎖。
  • 鎖池裡面的線程争搶對象鎖,但線程1什麼時候能搶到就不知道了。||||| 原本鎖池+第6步被喚醒的線程一起争搶對象鎖。
    線程的5種狀态(建立、就緒、運作、阻塞、死亡)

八.鎖池狀态

  1. 目前線程想調用對象A的同步方法時,發現對象A的鎖被别的線程占有,此時目前線程進入鎖池狀态。簡言之,鎖池裡面放的都是想争奪對象鎖的線程。
  2. 當一個線程1被另外一個線程2喚醒時,1線程進入鎖池狀态,去争奪對象鎖。
  3. 鎖池是在同步的環境下才有的概念,一個對象對應一個鎖池。

九.幾個方法的比較

  1. Thread.sleep(long millis),一定是目前線程調用此方法,目前線程進入阻塞,但不釋放對象鎖,millis後線程自動蘇醒進入可運作狀态。作用:給其它線程執行機會的最佳方式。
  2. Thread.yield(),一定是目前線程調用此方法,目前線程放棄擷取的cpu時間片,由運作狀态變會可運作狀态,讓OS再次選擇線程。作用:讓相同優先級的線程輪流執行,但并不保證一定會輪流執行。實際中無法保證yield()達到讓步目的,因為讓步的線程還有可能被線程排程程式再次選中。Thread.yield()不會導緻阻塞。
  3. t.join()/t.join(long millis),目前線程裡調用其它線程1的join方法,目前線程阻塞,但不釋放對象鎖,直到線程1執行完畢或者millis時間到,目前線程進入可運作狀态。
  4. obj.wait(),目前線程調用對象的wait()方法,目前線程釋放對象鎖,進入等待隊列。依靠notify()/notifyAll()喚醒或者wait(long timeout)timeout時間到自動喚醒。
  5. obj.notify()喚醒在此對象螢幕上等待的單個線程,選擇是任意性的。notifyAll()喚醒在此對象螢幕上等待的所有線程。

線程狀态:

    

線程的5種狀态(建立、就緒、運作、阻塞、死亡)

線程從建立、運作到結束總是處于下面五個狀态之一:建立狀态、就緒狀态、運作狀态、阻塞狀态及死亡狀态。

    1.建立狀态(New): 

        當用new操作符建立一個線程時, 例如new Thread(r),線程還沒有開始運作,此時線程處在建立狀态。 當一個線程處于新生狀态時,程式還沒有開始運作線程中的代碼

     2.就緒狀态(Runnable)

        一個新建立的線程并不自動開始運作,要執行線程,必須調用線程的start()方法。當線程對象調用start()方法即啟動了線程,start()方法建立線程運作的系統資源,并排程線程運作run()方法。當start()方法傳回後,線程就處于就緒狀态。

        處于就緒狀态的線程并不一定立即運作run()方法,線程還必須同其他線程競争CPU時間,隻有獲得CPU時間才可以運作線程。因為在單CPU的計算機系統中,不可能同時運作多個線程,一個時刻僅有一個線程處于運作狀态。是以此時可能有多個線程處于就緒狀态。對多個處于就緒狀态的線程是由Java運作時系統的線程排程程式(thread scheduler)來排程的。

    3.運作狀态(Running)

        當線程獲得CPU時間後,它才進入運作狀态,真正開始執行run()方法.

    4. 阻塞狀态(Blocked)

        線程運作過程中,可能由于各種原因進入阻塞狀态:

        1>線程通過調用sleep方法進入睡眠狀态;

        2>線程調用一個在I/O上被阻塞的操作,即該操作在輸入輸出操作完成之前不會傳回到它的調用者;

        3>線程試圖得到一個鎖,而該鎖正被其他線程持有;

        4>線程在等待某個觸發條件;

        ......           

        所謂阻塞狀态是正在運作的線程沒有運作結束,暫時讓出CPU,這時其他處于就緒狀态的線程就可以獲得CPU時間,進入運作狀态。

    5. 死亡狀态(Dead)

        有兩個原因會導緻線程死亡:

         1) run方法正常退出而自然死亡,

         2) 一個未捕獲的異常終止了run方法而使線程猝死。

        為了确定線程在目前是否存活着(就是要麼是可運作的,要麼是被阻塞了),需要使用isAlive方法。如果是可運作或被阻塞,這個方法傳回true; 如果線程仍舊是new狀态且不是可運作的, 或者線程死亡了,則傳回false.

轉:https://blog.csdn.net/xingjing1226/article/details/81977129

繼續閱讀