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分紅Runnable和Running兩個狀態(固然,你也能夠換成其餘的,這裏我只是本身好理解),那麼上面的線程轉換圖就轉變成了下面這樣(參考《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
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();
}
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()。這個方法能夠說是至關的霸道,給人一種「我無論,我就是要你如今馬上死亡(指線程)」的感受,而且其還會釋放線程全部的鎖資源,這樣可能會致使出現數據不一致從而出現線程不安全的狀況,以下面例子。線程
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