Java線程狀態和關閉線程的正確姿式

一、線程狀態及切換

  Java中的線程有六種狀態,使用線程Thread內的枚舉類來實現,以下,我對每一個狀態都進行了必定的解釋。html

  public enum State { /** 表示一個線程還沒啓用(即未調用start方法)*/ NEW, /** * JVM中執行的線程都是處於這個狀態的,可是處於這個狀態不必定在JVM中執行, * 也就是說,只有這個狀態有資格被JVM調度從而得到時間片執行。 */ RUNNABLE, /** * 線程在等待獲取鎖資源從而進入阻塞狀態, * 在這個狀態中,其一直監視鎖的動態,隨時準備搶佔鎖 * 若得到鎖資源,從新進入RUNNABLE狀態 */ BLOCKED, /** * 當調用Object.wait、Thread.join或者LockSupport類的park方法的時候,線程進入此狀態, * 該狀態若無其餘線程主動喚醒,則無期限的等待。 * 喚醒的方法包括:Object.notify(喚醒隨機一個)、Object.notifyAll(喚醒所有線程), * 被喚醒的線程從新進入RUNNABLE狀態 */ WAITING, /** * 同WAITING狀態,不過不一樣的是調用的方法加上了時間的限制, * 例如:Object.wait(10)、Thread.sleep(10)、Thread.join(10)、LockSupport.parkNanos(10)、LockSupport.parkUntil(10)這些方法 * 喚醒的方法有兩種: * 一、時間過時。 * 二、其餘線程調用了notify或者notifyAll * 喚醒以後一樣進入RUNNABLE狀態 */ TIMED_WAITING, /** 線程的終點(正常死亡或者被終止)*/ TERMINATED; }

   除了NEW和TERMINATED以外,其餘的狀態都是能夠相互轉換的,其轉換過程以下圖所示編程

 

  這裏特別講一下RUNNABLE狀態,在這個狀態中線程並不必定在執行程序,只有被JVM調度的線程才能得到執行的時間片,而且只有這個狀態的線程纔可以得到時間片,換句話說,被JVM調度而且得到時間片是隻屬於處於RUNNABLE狀態線程的權利。爲了便於理解,能夠將RUNNABLE分紅RunnableRunning兩個狀態(固然,你也能夠換成其餘的,這裏我只是本身好理解),那麼上面的線程轉換圖就轉變成了下面這樣(參考《Java併發編程的藝術》中的線程狀態圖):安全

 

   關於線程狀態轉換的例子,能夠經過下面的代碼加深理解併發

public class Test { public static void main(String[] args) { Test test = new Test(); // 1.NEW狀態
        Thread thread = new Thread(() -> { // 3.進行test對象鎖的爭奪,若搶到鎖則繼續執行,不然進入BLOCKED狀態監控該鎖,從新得到後進入RUNNABLE 
            synchronized (test) { try { // 4.進入TIMED_WAITING狀態,100ms後從新進入RUNNABLE狀態爭奪時間片 
                    Thread.sleep(100); // 5.從新得到時間片以後,進入WAITING狀態 
 test.wait(); } catch (InterruptedException e) { e.printStackTrace(); } } // 6.正常run()方法執行完畢後線程結束,進入TERMINATED 
 }); // 2.調用start()方法,線程進入RUNNABLE狀態
 thread.start(); }
}

注:代碼執行的順序爲註釋的序號

二、正確的結束一個線程

  在上面的例子中咱們看到線程的run方法正常執行完畢以後線程就正常死亡進入TERMINATED狀態了,那麼若是咱們有中途中止線程的需求,咱們應該如何正確的結束一個線程呢?spa

  1. 使用interrupt()方法:在線程內部,其定義了一個變量來標識當前線程是否處於被打斷狀態,調用interrupt()方法則使這個狀態變爲true;換句話說,線程在阻塞狀態的時候會檢查標識狀態,若是是true,則拋出換句話說,線程在阻塞狀態的時候會檢查標識狀態,若是是true,則拋出InterruptedException,不然阻塞,因此不會打斷非阻塞的線程,即對正在運行的線程此方法無效。咱們採用這個方法加異常處理的方式來結束一個線程。
      public static void main(String[] args) { Thread thread = new Thread(() -> { try { Thread.sleep(1); } catch (InterruptedException e) { e.printStackTrace(); // 這裏的return是必須的,緣由後面說明
                    return; } System.err.println("thread interrupt test..."); }); thread.start(); thread.interrupt(); System.out.println("main thread end..."); }

    // 結果圖:異常後面的語句不會打印


      這裏關於線程中的打斷標識變量(以後以interrupt稱)須要說明的是,在特定的狀況下其狀態會被重置。
       一、線程內部在catch了異常了以後interrupt的狀態會被重置爲false。
    二、線程調用了Thread.interrupted()方法以後,interrupt的狀態會被重置爲false。若是須要判斷線程是否中斷的話可使用對象方法isInterrupted(),此方法不會重置。
    因此在剛纔的代碼中須要加入return來結束線程,不然的話線程仍是會繼續往下執行,以下圖


    使用isInterrupted()實現:
    public static void main(String[] args) throws InterruptedException {
    Thread thread = new Thread(() -> {
    while (!Thread.currentThread().isInterrupted()) {
    int k = 0;
    while (k++ < 10) {
    System.out.println("do something..." + k);
    }
    }
    System.err.println("thread end...");
    });
    thread.start();
    Thread.sleep(1);
    // 主線程流程執行完了,須要中止線程
    thread.interrupt();
    }

      

  2. 使用標識位來實現:定義一個變量標識線程是否終止,若終止了則退出run方法。跟上面isInterrupted()的實現同樣,不過換成了volatile變量而已。
    public class Test { public static volatile boolean interrupted = false; public static void main(String[] args) throws InterruptedException { Thread thread = new Thread(() -> { while (!interrupted) { int k = 0; while (k++ < 10) { if (interrupted) { System.err.println("thread invoke end...."); return; } System.out.println("do something..." + k); } } System.err.println("thread end..."); }); thread.start(); Thread.sleep(1); // 主線程流程執行完了,須要中止線程
            interrupted = true; } }
    // 結果圖

三、stop()方法——不正確的線程中斷方法

    在線程提供的方法中還有一個方法能夠強制關閉線程——stop()。這個方法能夠說是至關的霸道,給人一種「我無論,我就是要你如今馬上死亡(指線程)」的感受,而且其還會釋放線程全部的鎖資源,這樣可能會致使出現數據不一致從而出現線程不安全的狀況,以下面例子。線程

public class Test { public static volatile boolean flag = false; public int state = 0; public static void main(String[] args) throws InterruptedException { Test test = new Test(); Thread thread = new Thread(() -> { synchronized (test) { try { test.state = 1; Thread.sleep(100); if (flag) { test.state = 2; } System.err.println("thread execute finished..."); } catch (InterruptedException e) { e.printStackTrace(); } } }); thread.start(); Thread.sleep(1); thread.stop(); flag = true; System.out.println("state狀態:" + test.state); } }
// 在這段代碼中,進入線程時默認將state賦爲1,接着過一段時間後若是觸發了特定條件則把state賦爲2,可是在特定條件觸發以前,線程就被終止掉了,這個特定條件雖然符合但卻沒辦法執行,從而致使數據的不一致。
// 結果圖
  

  因此,咱們應該採用上面兩種正確的方式而不是stop()來停止線程。此外,stop()方法若在線程start()以前執行,那麼在線程啓動的時候就會當即死亡。

 

 

如有不對之處,望各位不吝指教(反正免費,對吧)。code

原文出處:https://www.cnblogs.com/zhangweicheng/p/11695849.htmlhtm

相關文章
相關標籤/搜索