一塊兒學併發編程 - 處理異常停止的線程

單線程的開發過程當中,一般採用try-catch的方式進行異常捕獲,可是這種方式在多線程環境中會顯得無能爲力,並且還有可能致使一些問題的出現,好比發生異常的時候不能及時回收系統資源,或者沒法及時關閉當前的鏈接...java

<!-- more -->git

概述

Java中有兩種異常,即已知異常(編輯器會提示捕獲或者拋出)未知異常(特殊狀況下發生),因爲線程中的run()方法是不接受拋出語句的(只能內部捕獲),因此在面對未知異常的狀況,線程默認的會將堆棧跟蹤信息輸出到控制檯中(或者記錄到錯誤日誌文件中)而後退出程序。微信

JDK1.5以前,不能爲線程單獨設置或指定一個默認的UncaughtExceptionHandler,爲了設置UncaughtExceptionHandler,須要繼承ThreadGroup並覆寫uncaughtException方法。 幸運的是JDK1.5後線程提供了一個setUncaughtExceptionHandler方法,用來捕獲並處理因線程中拋出的未知異常,以免程序終止。多線程

案例

1.首先模擬一個鏈接池,提供異步

class ConnectionPool {
    static void create() {
        System.out.println("初始化鏈接池...");
    }
    static void close() {
        System.out.println("關閉鏈接池...");
    }
}

2.爲了測試須要,只是簡單模擬了一個異常編輯器

public static void main(String[] args) {
    ConnectionPool.create();
    try {
        //有個任務須要異步執行
        Thread thread = new Thread(() -> System.out.println(Integer.parseInt("ABC")), "T2");
        thread.start();
    } catch (Exception e) {
        ConnectionPool.close();
    }
}

錯誤信息

分析: 從日誌中,並未發現關閉資源應有的日誌輸出,很明顯try-catch沒有起做用,由於在main函數中他是主線程,當thread.start()以後,主線程的代碼與子線程就沒半毛錢關係了,因此發生在子線程內部的錯誤沒法捕獲到。函數

解決方案

使用UncaughtExceptionHandler,這裏爲了偷懶使用了lambda簡化了匿名內部類的寫法(也能夠實現UncaughtExceptionHandler測試

public static void main(String[] args) {
    ConnectionPool.create();
    Thread thread = new Thread(() -> System.out.println(Integer.parseInt("ABC")), "T1");
    thread.start();
    thread.setUncaughtExceptionHandler((t, e) -> {
        System.out.println("[線程] - [" + t.getName() + "] - [消息] - [" + e.getMessage() + "]");
        ConnectionPool.close();
    });
}

正確捕獲

分析: 從日誌中能夠發現錯誤信息被咱們捕獲了,而且能夠成功釋放資源!使用UncaughtExceptionHandler,能夠捕獲到未知異常且記錄下自定義的日誌(默認拋出堆棧信息),具體在Zookeeper中就有使用過,源碼爲:ZookeeperThreadZooKeeperCriticalThread,有興趣能夠去看看...spa

- 說點什麼

全文代碼:https://gitee.com/battcn/battcn-concurent/tree/master/Chapter1-1/battcn-thread/src/main/java/com/battcn/chapter11線程

  • 我的QQ:1837307557
  • battcn開源羣(適合新手):391619659

微信公衆號:battcn(歡迎調戲)

相關文章
相關標籤/搜索