JAVA多線程之中斷機制(如何處理中斷?)

来源:http://www.cnblogs.com/hapjin/archive/2016/05/01/5450779.html
-Advertisement-
Play Games

一,介紹 這篇文章主要記錄使用 interrupt() 方法中斷線程,以及如何對InterruptedException進行處理。感覺對InterruptedException異常進行處理是一件謹慎且有技巧的活兒。 由於使用stop()方法停止線程非常的暴力,人家線程運行的好好的,突然就把人家殺死了 ...


一,介紹

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

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

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

 

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

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

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

下麵是一些例子,這些例子摘自《JAVA多線程核心編程技術》

 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(2000);
 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線程睡眠2000ms後,執行第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行語句不會執行)。

因此,上面就是一個採用拋出異常的方式來結束線程的示例。儘管該示例的實用性不大。原因在 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


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 1.繼上一篇隨筆,鏈接點我,解決手機端cookie的問題。 2.上次用cookie+redis實現了session,並且手機瀏覽器可能回傳cookies有問題,所以最後用js取出cookie跟在請求的url後面。 3.但是今天發現了新的問題,js取cookie存的sessionId為空,情況如下: ...
  • 今天在做一個聯繫人管理的C#設計時,遇到了這個問題,我需要將父窗體中的textBox中的值傳到子窗體併進行資料庫查詢操作,我用了new 父窗體().textBox.text;來進行值傳遞,然而並無卵用,經過多次試驗,找到了一個比較簡單的解決方法: 父窗體:Logout 子窗體:Affirm 父窗體文 ...
  • 1 1 <appSettings> 2 2 <add key="webpages:Version" value="2.0.0.0" /> 3 3 <add key="webpages:Enabled" value="false" /> 4 4 <add key="PreserveLoginUrl" ...
  • 面試的時候遇到的一個題目: 函數類型:List<string> FindOut(string s1, Dictionary<string,int> dict1) 題目要求:將沒有分隔符的字元串s1根據字典dict1進行拆分單詞,若能夠完全拆分,則依次輸出拆分後的單詞;若不能完全拆分,則輸出null。 ...
  • 最近工作的時候遇到一個問題,根據Web端接收到的對象obj1,更新對應的對象值ogj2。先判斷obj1中屬性值是否為null, 若不等於null,則更新obj2中對應屬性值;若等於null,則保持obj2中對應屬性值不變。 先創建Student Class: 1 using System; 2 us ...
  • 垃圾回收主要考慮三件事情:哪些記憶體需要回收?什麼時候回收?如何回收? 一、哪些記憶體需要回收? 堆記憶體:對於JVM 來說,垃圾回收主要是針對堆記憶體中的對象實例。 方法區:垃圾收集行為在方法區是比較少出現的,一般來說,這個區域的回收“成績”比較難以令人滿意,尤其是類型的卸載,條件相當苛刻,但是這部分區域 ...
  • 1、mongodb在mac上的安裝 下載mongodb,https://www.mongodb.org/ 解壓縮到一個指定文件夾,如:/Users/enniu1/Desktop/zjg/mongodb-osx-x86_64-3.2.6(這是我的mongodb的版本) 配置PATH 輸入命令:"vi ...
  • windows環境下: 1,Window中修改startup.bat文件,在頂部添加如下: SET CATALINA_OPTS=-server -Xdebug -Xnoagent -Djava.compiler=NONE -Xrunjdwp:transport=dt_socket,server=y, ...
一周排行
    -Advertisement-
    Play Games
  • 示例項目結構 在 Visual Studio 中創建一個 WinForms 應用程式後,項目結構如下所示: MyWinFormsApp/ │ ├───Properties/ │ └───Settings.settings │ ├───bin/ │ ├───Debug/ │ └───Release/ ...
  • [STAThread] 特性用於需要與 COM 組件交互的應用程式,尤其是依賴單線程模型(如 Windows Forms 應用程式)的組件。在 STA 模式下,線程擁有自己的消息迴圈,這對於處理用戶界面和某些 COM 組件是必要的。 [STAThread] static void Main(stri ...
  • 在WinForm中使用全局異常捕獲處理 在WinForm應用程式中,全局異常捕獲是確保程式穩定性的關鍵。通過在Program類的Main方法中設置全局異常處理,可以有效地捕獲並處理未預見的異常,從而避免程式崩潰。 註冊全局異常事件 [STAThread] static void Main() { / ...
  • 前言 給大家推薦一款開源的 Winform 控制項庫,可以幫助我們開發更加美觀、漂亮的 WinForm 界面。 項目介紹 SunnyUI.NET 是一個基於 .NET Framework 4.0+、.NET 6、.NET 7 和 .NET 8 的 WinForm 開源控制項庫,同時也提供了工具類庫、擴展 ...
  • 說明 該文章是屬於OverallAuth2.0系列文章,每周更新一篇該系列文章(從0到1完成系統開發)。 該系統文章,我會儘量說的非常詳細,做到不管新手、老手都能看懂。 說明:OverallAuth2.0 是一個簡單、易懂、功能強大的許可權+可視化流程管理系統。 有興趣的朋友,請關註我吧(*^▽^*) ...
  • 一、下載安裝 1.下載git 必須先下載並安裝git,再TortoiseGit下載安裝 git安裝參考教程:https://blog.csdn.net/mukes/article/details/115693833 2.TortoiseGit下載與安裝 TortoiseGit,Git客戶端,32/6 ...
  • 前言 在項目開發過程中,理解數據結構和演算法如同掌握蓋房子的秘訣。演算法不僅能幫助我們編寫高效、優質的代碼,還能解決項目中遇到的各種難題。 給大家推薦一個支持C#的開源免費、新手友好的數據結構與演算法入門教程:Hello演算法。 項目介紹 《Hello Algo》是一本開源免費、新手友好的數據結構與演算法入門 ...
  • 1.生成單個Proto.bat內容 @rem Copyright 2016, Google Inc. @rem All rights reserved. @rem @rem Redistribution and use in source and binary forms, with or with ...
  • 一:背景 1. 講故事 前段時間有位朋友找到我,說他的窗體程式在客戶這邊出現了卡死,讓我幫忙看下怎麼回事?dump也生成了,既然有dump了那就上 windbg 分析吧。 二:WinDbg 分析 1. 為什麼會卡死 窗體程式的卡死,入口門檻很低,後續往下分析就不一定了,不管怎麼說先用 !clrsta ...
  • 前言 人工智慧時代,人臉識別技術已成為安全驗證、身份識別和用戶交互的關鍵工具。 給大家推薦一款.NET 開源提供了強大的人臉識別 API,工具不僅易於集成,還具備高效處理能力。 本文將介紹一款如何利用這些API,為我們的項目添加智能識別的亮點。 項目介紹 GitHub 上擁有 1.2k 星標的 C# ...