任務和線程的啓動很容易。在大多數時候,咱們都會讓它們運行直到結束,或者讓它們自行中止。然而,有時候咱們但願提早結束任務或線程,或許是由於用戶取消了操做,或者應用程序須要被快速關閉。 要使任務和線程能安仝、快速、可靠地中止下來,並非一件容易的事。Java的Thread類爲咱們提供了stop(),suspend()等中止掛起線程的方法,可是因爲安全問題目前都已被棄用。Java並無提供一種安全的搶佔式方法來中止線程,但它提供了中斷(Interruption),這是一種協做機制,採用協做式的方式使一個線程終止另外一個線程的當前工做。 這種協做式的方法是必要的,咱們不多但願某個任務、線程或服務當即中止,由於這種當即中止會使共享的數據結構處於不一致的狀態。相反,在編寫任務和服務時可使用一種協做的方式:當須要中止時,它們首先會清除當前正在執行的工做,而後再結束。這提供了更好的靈活性,由於任務自己的代碼比發出取消請求的代碼更清楚如何執行清除工做。 生命週期結束(End-of-Lifecycle)的問題會使任務、服務以及程序的設計和實現等過程變得複雜,而這個在程序設計中很是重要的要素卻常常被忽略。一個在行爲良好的軟件與勉強運行的軟件之間的最主要區別就是,行爲良好的軟件能很完善地處理失敗、關閉和取消等過程。java
如何設計一種協做機制,讓線程能夠安全的中斷呢?咱們能夠設置一個取消標誌,在工做線程會被中斷的地方去檢查這個標誌,當檢查到這個中斷標誌被設置爲已取消時,工做線程便開始作取消工做。編程
1 public class CancelableThread implements Runnable { 2 3 //線程取消標誌,volatile修飾,保證內存可見性 4 private volatile boolean isCanceled = false; 5 6 @Override 7 public void run() { 8 while (!isCanceled) {//在工做線程中輪詢檢測這個取消標誌 9 System.out.println("The current thread is doing something..."); 10 System.out.println(Thread.currentThread().getName() + " cancel flag is " + isCanceled); 11 } 12 //當取消標誌被設置爲true,執行如下代碼,能夠作一些取消工做 13 System.out.println(Thread.currentThread().getName() + "The current thread Has been cancelled"); 14 } 15 16 private void cancel() { 17 isCanceled = true; 18 } 19 }
1 public class Main { 2 public static void main(String[] args) throws Exception { 3 4 CancelableThread cancelableThread = new CancelableThread(); 5 new Thread(cancelableThread).start(); 6 try { 7 Thread.sleep(1); 8 } finally { 9 //設置標誌位爲true,中斷線程 10 cancelableThread.cancel(); 11 } 12 } 13 }
打印結果:安全
Thread-0 cancel flag is false The current thread is doing something... Thread-0 cancel flag is false The current thread is doing something... Thread-0 cancel flag is false The current thread is doing something... Thread-0 cancel flag is false The current thread is doing something... Thread-0 cancel flag is true Thread-0The current thread Has been cancelled
其實Thread類爲咱們提供了三個與線程中斷相關的方法,來實現上述機制。這三個方法分別是:數據結構
public void interrupt() { //... 省略相關代碼 interrupt0(); // Just to set the interrupt flag //... 省略相關代碼 }
public static boolean interrupted() { return currentThread().isInterrupted(true); }
public boolean isInterrupted() { return isInterrupted(false); }
查看源碼發現,靜態的interrupted()和isInterrupted()方法都是調用的 private native boolean isInterrupted(boolean ClearInterrupted); 根據傳入的ClearInterrupted的值,來判斷是否要清除中斷標誌位。併發
1 public class InterruptTest { 2 3 4 static class InnerThread extends Thread{ 5 6 7 @Override 8 public void run() { 9 while(!isInterrupted()){ 10 System.out.println(Thread.currentThread().getName()+" cancle flag is "+isInterrupted()); 11 try { 12 13 Thread.sleep(100); 14 15 }catch (InterruptedException e){ 16 e.printStackTrace(); 17 //拋出InterruptedException,中斷標誌位被清除,再次調用 interrupt(); 18 interrupt(); 19 } 20 } 21 System.out.println(Thread.currentThread().getName()+" cancle flag is "+isInterrupted()); 22 23 } 24 } 25 26 27 public static void main(String[] args) { 28 InnerThread innerThread = new InnerThread(); 29 innerThread.start(); 30 try { 31 32 Thread.sleep(1000); 33 34 }catch (InterruptedException e){ 35 e.printStackTrace(); 36 } 37 innerThread.interrupt(); 38 // InnerThread innerThread2 = new InnerThread(); 39 // innerThread2.start(); 40 // innerThread2.interrupt(); 41 } 42 }
打印結果:ide
Thread-0 cancle flag is false Thread-0 cancle flag is false Thread-0 cancle flag is false Thread-0 cancle flag is false Thread-0 cancle flag is false Thread-0 cancle flag is false Thread-0 cancle flag is false Thread-0 cancle flag is false Thread-0 cancle flag is false Thread-0 cancle flag is false java.lang.InterruptedException: sleep interrupted at java.lang.Thread.sleep(Native Method) at InterruptTest$InnerThread.run(InterruptTest.java:13) Thread-0 cancle flag is true
參考資料:《Java併發編程實戰》