JAVA進程中斷怎麼處理

轉載:https://www.cnblogs.com/hapjin/p/5450779.html

           https://www.cnblogs.com/yangming1996/p/7612653.html

一,介紹

這篇文章主要記錄使用 interrupt() 方法中斷線程,以及如何對InterruptedException進行處理。感覺對InterruptedException異常進行處理是一件謹慎且有技巧的活兒。

由於使用stop()方法停止線程非常的暴力,人家線程運行的好好的,突然就把人家殺死了,線程佔用的鎖被強制釋放,極易導致數據的不一致性。可參考這篇文章對stop()方法的介紹。

因此,提出了一種溫和的方式:請求另外一個線程不要再執行了,這就是中斷方式。

 

二,中斷及如何響應中斷?

如何優雅地響應中斷真的是太高深了,看到這篇文章:Java 理論與實踐: 處理 InterruptedException就嚇了一跳。下面只是記錄一些最簡單的我對響應中斷的理解。

假設某個線程要不停地處理某件事情(比如 i 一直自增),但是還有個要求:在處理事情前,先要檢查下這個線程是否被中斷,如果已經被中斷,處理就應該結束。

下面是一些例子,這些例子摘自書本:

複製代碼

 1 public class Run {
 2 
 3     public static void main(String[] args) {
 4         try {
 5             MyThread thread = new MyThread();
 6             thread.start();
 7             Thread.sleep(20);//modify 2000 to 20
 8             thread.interrupt();//請求中斷MyThread線程
 9         } catch (InterruptedException e) {
10             System.out.println("main catch");
11             e.printStackTrace();
12         }
13         System.out.println("end!");
14     }
15 }

複製代碼

main線程睡眠20ms後,執行第8行中斷MyThread線程。

 

複製代碼

 1 public class MyThread extends Thread {
 2     @Override
 3     public void run() {
 4         super.run();
 5         for (int i = 0; i < 500000; i++) {
 6             if (this.interrupted()) {
 7                 System.out.println("should be stopped and exit");
 8                 break;
 9             }
10             System.out.println("i=" + (i + 1));
11         }
12         System.out.println("this line is also executed. thread does not stopped");//儘管線程被中斷,但並沒有結束運行。這行代碼還是會被執行
13     }
14 }

複製代碼

當MyThread獲得CPU執行時,第6行的 if 測試中,檢測到中斷標識被設置。即MyThread線程檢測到了main線程想要中斷它的 請求。

大多數情況下,MyThread檢測到了中斷請求,對該中斷的響應是:退出執行(或者說是結束執行)。

但是,上面第5至8行for循環,是執行break語句跳出for循環。但是,線程並沒有結束,它只是跳出了for循環而已,它還會繼續執行第12行的代碼....

因此,我們的問題是,當收到了中斷請求後,如何結束該線程呢?

一種可行的方法是使用 return 語句 而不是 break語句。。。。。哈哈。。。

當然,一種更優雅的方式則是:拋出InterruptedException異常。

看下面MyThread類的代碼:

複製代碼

 1 public class MyThread extends Thread {
 2     @Override
 3     public void run() {
 4         super.run();
 5         try{
 6             for (int i = 0; i < 500000; i++) {
 7                 if (this.interrupted()) {
 8                     System.out.println("should be stopped and exit");
 9                     throw new InterruptedException();
10                 }
11                 System.out.println("i=" + (i + 1));
12             }
13             System.out.println("this line cannot be executed. cause thread throws exception");//這行語句不會被執行!!!
14         }catch(InterruptedException e){
15             System.out.println("catch interrupted exception");
16             e.printStackTrace();
17         }
18     }
19 }

複製代碼

當MyThread線程檢測到中斷標識爲true後,在第9行拋出InterruptedException異常。這樣,該線程就不能再執行其他的正常語句了(如,第13行語句不會執行)。這裏表明:interrupt()方法有兩個作用,一個是將線程的中斷狀態置位(中斷狀態由false變成true);另一個則是:讓被中斷的線程拋出InterruptedException異常。

這是很重要的。這樣,對於那些阻塞方法(比如 wait() 和 sleep())而言,當另一個線程調用interrupt()中斷該線程時,該線程會從阻塞狀態退出並且拋出中斷異常。這樣,我們就可以捕捉到中斷異常,並根據實際情況對該線程從阻塞方法中異常退出而進行一些處理。

比如說:線程A獲得了鎖進入了同步代碼塊中,但由於條件不足調用 wait() 方法阻塞了。這個時候,線程B執行 threadA.interrupt()請求中斷線程A,此時線程A就會拋出InterruptedException,我們就可以在catch中捕獲到這個異常並進行相應處理(比如進一步往上拋出)

 

因此,上面就是一個採用拋出異常的方式來結束線程的示例。儘管該示例的實用性不大。原因在 IBM的這篇博文中:我們 生吞了中斷。

在第14行,我們直接catch了異常,然後打印輸出了一下而已,調用棧中的更高層的代碼是無法獲得關於該異常的信息的。

第16行的e.printStackTrace()作用就相當於

“(僅僅記錄 InterruptedException 也不是明智的做法,因爲等到人來讀取日誌的時候,再來對它作出處理就爲時已晚了。)”---摘自參考博文

 

上面我們是在run()方法中拋出異常,符合這裏描述的:

有時候拋出 InterruptedException 並不合適,例如當由 Runnable 定義的任務調用一個
可中斷的方法時,就是如此。在這種情況下,不能重新拋出 InterruptedException,但是
您也不想什麼都不做。當一個阻塞方法檢測到中斷並拋出 InterruptedException 時,它
清除中斷狀態。如果捕捉到 InterruptedException 但是不能重新拋出它,那麼應該保留
中斷髮生的證據,以便調用棧中更高層的代碼能知道中斷,並對中斷作出響應。該任務可以
通過調用 interrupt() 以 “重新中斷” 當前線程來完成,如清單 3 所示。 -----“摘自參考博文”

 

因爲,run方法是實現的Runnable接口中的方法。不能像下面這樣定義,也即上面所說的:“不能重新拋出InterruptedException”。

        @Override
        public void run() throws InterruptedException{//這是錯誤的
          //do something...

 

因此,一個更好的解決方案是:調用 interrupt() 以 “重新中斷” 當前線程。改進MyThread類中catch異常的方式,如下:

複製代碼

 1 public class MyThread extends Thread {
 2     @Override
 3     public void run() {
 4         super.run();
 5         try{
 6             for (int i = 0; i < 500000; i++) {
 7                 if (this.interrupted()) {
 8                     System.out.println("should be stopped and exit");
 9                     throw new InterruptedException();
10                 }
11                 System.out.println("i=" + (i + 1));
12             }
13             System.out.println("this line cannot be executed. cause thread throws exception");
14         }catch(InterruptedException e){
15             /**這樣處理不好
16              * System.out.println("catch interrupted exception");
17              * e.printStackTrace();
18              */
19              Thread.currentThread().interrupt();//這樣處理比較好
20         }
21     }
22 }

複製代碼

這樣,就由 生吞異常 變成了 將 異常事件 進一步擴散了。

 

參考博文:Java 理論與實踐: 處理 InterruptedException

參考書籍:《Java多線程編程核心技術》

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章