java 線程的幾種狀態


 

java thread的運行周期中, 有幾種狀態, 在 java.lang.Thread.State 中有詳細定義和說明:

NEW 狀態是指線程剛創建, 尚未啟動

RUNNABLE 狀態是線程正在正常運行中, 當然可能會有某種耗時計算/IO等待的操作/CPU時間片切換等, 這個狀態下發生的等待一般是其他系統資源, 而不是鎖, Sleep等

BLOCKED  這個狀態下, 是在多個線程有同步操作的場景, 比如正在等待另一個線程的synchronized 塊的執行釋放, 或者可重入的 synchronized塊里別人調用wait() 方法, 也就是這里是線程在等待進入臨界區

WAITING  這個狀態下是指線程擁有了某個鎖之后, 調用了他的wait方法, 等待其他線程/鎖擁有者調用 notify / notifyAll 一遍該線程可以繼續下一步操作, 這里要區分 BLOCKED 和 WATING 的區別, 一個是在臨界點外面等待進入, 一個是在理解點里面wait等待別人notify, 線程調用了join方法 join了另外的線程的時候, 也會進入WAITING狀態, 等待被他join的線程執行結束

TIMED_WAITING  這個狀態就是有限的(時間限制)的WAITING, 一般出現在調用wait(long), join(long)等情況下, 另外一個線程sleep后, 也會進入TIMED_WAITING狀態

TERMINATED 這個狀態下表示 該線程的run方法已經執行完畢了, 基本上就等於死亡了(當時如果線程被持久持有, 可能不會被回收)

下面談談如何讓線程進入以上幾種狀態:

1.  NEW, 這個最簡單了,  
 
      static  void NEW() {
          Thread t =  new Thread ();
         System.  out.println(t.getState());
    }
 
輸出NEW
 
2.  RUNNABLE, 也簡單, 讓一個thread start, 同時代碼里面不要sleep或者wait等
 
    private  static  void RUNNABLE() {
         Thread t =  new Thread(){
             
               public  void run(){
                   for( int i=0; i<Integer. MAX_VALUE; i++){
                      System.  out.println(i);
                 }
             }
             
         };
         
         t.start();
    }
 
  71e94764e28c7f8bbd3ef91c1c0088b4
 
3.  BLOCKED, 這個就必須至少兩個線程以上, 然后互相等待synchronized 塊
          
      private  static  void BLOCKED() {
         
           final Object lock =  new Object();
         
         Runnable run =  new Runnable() {
             
              @Override
               public  void run() {
                   for( int i=0; i<Integer. MAX_VALUE; i++){
                      
                        synchronized (lock) {
                          System.  out.println(i);
                      }
                      
                 }
             }
         };
         
         Thread t1 =  new Thread(run);
         t1.setName( “t1”);
         Thread t2 =  new Thread(run);
         t2.setName( “t2”);
         
         t1.start();
         t2.start();
         
    }
 
8e9ad1eadf9d38c0b6c8cb024cb36c0c
這時候, 一個在RUNNABLE, 另一個就會在BLOCKED (等待另一個線程的 System.out.println.. 這是個IO操作, 屬於系統資源, 不會造成WAITING等)
 
4.  WAITING, 這個需要用到生產者消費者模型, 當生產者生產過慢的時候, 消費者就會等待生產者的下一次notify
 
      private  static  void WAITING() {
 
           final Object lock =  new Object();
         Thread t1 =  new Thread(){
              @Override
               public  void run() {
                 
                   int i = 0;
                 
                   while( true ){
                        synchronized (lock) {
                            try {
                               lock.wait();
                          }  catch (InterruptedException e) {
                          }
                          System.  out.println(i++);
                      }
                 }
             }
         };
         
         Thread t2 =  new Thread(){
              @Override
               public  void run() {
                 
                   while( true ){
                        synchronized (lock) {
                            for( int i = 0; i< 10000000; i++){
                              System.  out.println(i);
                          }
                          lock.notifyAll();
                      }
                      
                 }
             }
         };
         
         t1.setName( “^^t1^^”);
         t2.setName( “^^t2^^”);
         
         t1.start();
         t2.start();
    }
 
  b43a3d9b67bab266ffea4537fb043bba
 
5.  TIMED_WAITING, 這個僅需要在4的基礎上, 在wait方法加上一個時間參數進行限制就OK了.
 
把4中的synchronized 塊改成如下就可以了.
 
synchronized (lock) {
    try {
      lock.wait(60 * 1000L);
   }  catch (InterruptedException e) {
   }
   System.  out .println(i++);
 }
 
  88d9047d8a709c2d63c695bcf58a0297
另外看stack的輸出,  他叫 TIMED_WAITING(on  object monitor) , 說明括號后面還有其他的情況, 比如sleep, 我們直接把t2的for循環改成sleep試試:
 
synchronized (lock) {
    
     try {
           sleep(30*1000L);
    }  catch (InterruptedException e) {
    }
    lock.notifyAll();
}
a37ef4c72c00e793f8b6c746d74fd4d9 
 
看到了吧, t2的state是 TIMED_WAITING( sleeping),  而t1依然是on object monitor , 因為t1還是wait在等待t2 notify, 而t2是自己sleep
 
另外, join操作也是進入 on object monitor
 
6.  TERMINATED, 這個狀態只要線程結束了run方法, 就會進入了…
 
     private  static  void TERMINATED() {
         Thread t1 =  new Thread();
         t1.start();
         System.  out.println(t1.getState());
           try {
             Thread.  sleep(1000L);
         }  catch (InterruptedException e) {
         }
         System.  out.println(t1.getState());
    }
輸出: 
RUNNABLE
TERMINATED
 
由於線程的start方法是異步啟動的, 所以在其執行后立即獲取狀態有可能才剛進入RUN方法且還未執行完畢
 
 
廢話了這么多,  了解線程的狀態究竟有什么用?
所以說這是個釣魚貼么…
 
好吧, 一句話, 在找到系統中的潛在性能瓶頸有作用.
 
當java系統運行慢的時候, 我們想到的應該先找到性能的瓶頸, 而 jstack等工具, 通過jvm當前的stack可以看到當前整個vm所有線程的狀態, 當我們看到一個線程狀態經常處於
WAITING 或者 BLOCKED的時候, 要小心了, 他可能在等待資源經常沒有得到釋放(當然, 線程池的調度用的也是各種隊列各種鎖, 要區分一下, 比如下圖)
6db341bbd7680bbc2e6ae37a66329397
 
 
這是個經典的並發包里面的線程池, 其調度隊列用的是LinkedBlockingQueue, 執行take的時候會block住, 等待下一個任務進入隊列中, 然后進入執行, 這種理論上不是系統的性能瓶頸, 找瓶頸一般 先找自己的代碼stack, 再去排查那些開源的組件/JDK的問題
 
排查問題的幾個思路:
 
0. 如何跟蹤一個線程?
看到上面的stack輸出沒有, 第一行是內容是 threadName priority tid nid desc
更過跟蹤tid, nid 都可以唯一找到該線程.
 
1. 發現有線程進入BLOCK, 而且持續好久, 這說明性能瓶頸存在於synchronized塊中, 因為他一直block住, 進不去, 說明另一個線程一直沒有處理好, 也就這個synchronized塊中處理速度比較慢, 然后再深入查看. 當然也有可能同時block的線程太多, 排隊太久造成.
 
2. 發現有線程進入WAITING, 而且持續好久, 說明性能瓶頸存在於觸發notify的那段邏輯. 當然還有就是同時WAITING的線程過多, 老是等不到釋放.
 
3. 線程進入TIME_WAITING 狀態且持續好久的, 跟2的排查方式一樣.
 
 
上面的黑底白字截圖都是通過jstack打印出來的, 可以直接定位到你想知道的線程的執行棧, 這對java性能瓶頸的分析是有極大作用的.
 
 
NOTE: 上面所有代碼都是為了跟蹤線程的狀態而寫的, 千萬不要在線上應用中這么寫…

javajstackthread


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM