如何捕獲java線程中的逃逸的異常

來自:yexx

在java線程中,在run方法中,我們要在run()方法中,把一切的異常有處理掉,也就try-catch掉。不能讓這個線程拋出異常,因為如果我們不使用特殊的方式的話,我們是無法捕獲從這個線程中逃逸的異常的。異常一旦拋出了,那么這個線程就會停止運行,但是不會影響主線程和其它的線程。因為主線程和其它的線程都不知道它拋出了異常。 線程在run方法拋出異常,沒有catch。

線程在run方法拋出異常,沒有catch

那么會有疑問,是不是在main函數里面沒有catch

package Thread.UncaughtExceptionHandler;

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

/**
 * 測試線程拋異常不能捕獲
 * @author yxx
 */
public class ExceptionThread implements Runnable {

    @Override
    public void run() {
        System.out.println("我準備拋出異常,你能接到嗎?");
        throw new RuntimeException();
    }
    public static void main(String[] args) {
        try {
            Thread thread = new Thread(new ExceptionThread());
            ExecutorService exec = Executors.newCachedThreadPool();
            exec.execute(thread);
        } catch (Exception e) {
            System.out.println("我要捉住異常");
            e.printStackTrace();
        }
    }
}

一樣是catch不到的,想想一個線程的拋出異常之后就終止了如果再進行catch,都不在一個線程里面

所以這個時候我們得去實現UncaughtExceptionHandler這個接口來捕獲拋出的異常

實現UncaughtExceptionHandler這個接口來捕獲拋出的異常

ExceptionThread2.java

package Thread.UncaughtExceptionHandler;

/**
 * 一個拋出異常的線程類 但是想被抓住
 * @author yxx
 */
public class ExceptionThread2 implements Runnable {

    @Override
    public void run() {
        Thread t = Thread.currentThread();
        System.out.println("run by " + t.getName());
        System.out.println("Exception " + t.getUncaughtExceptionHandler());
        throw new RuntimeException();
    }
}

ExceptionThread3.java

package Thread.UncaughtExceptionHandler;

/**
 * 一個拋出異常的線程類 但是想被抓住
 * @author yxx
 */
public class ExceptionThread3 implements Runnable {

    @Override
    public void run() {
        Thread t = Thread.currentThread();
        System.out.println("run by " + t.getName());
        System.out.println("Exception " + t.getUncaughtExceptionHandler());
        throw new NullPointerException();
    }

}

MyUncaughtExceptionHandler.java

package Thread.UncaughtExceptionHandler;

import java.lang.Thread.UncaughtExceptionHandler;

/**
 * 實現UncaughtExceptionHandler接口,抓住拋出的異常
 * @author yxx
 *
 */
public class MyUncaughtExceptionHandler implements UncaughtExceptionHandler {

    @Override
    public void uncaughtException(Thread t, Throwable e) {
        System.out.println("caugth: " + e.toString());
    }
}

HandlerThreadFactory.java

package Thread.UncaughtExceptionHandler;

import java.util.concurrent.ThreadFactory;

/**
 * 實現ThreadFactory來創建線程,為每一個thread對象都附上一個未捕獲異常的處理器
 * @author yxx
 *
 */
public class HandlerThreadFactory implements ThreadFactory {

    @Override
    public Thread newThread(Runnable r) {
        System.out.println(this + "creating new thread");
        Thread t = new Thread(r);
        System.out.println("created" + t);
        //為每一個線程設置異常捕獲器
        t.setUncaughtExceptionHandler(new MyUncaughtExceptionHandler());
        System.out.println("eh = " + t.getUncaughtExceptionHandler());
        return t;
    }
}

CaptureUncaughtException.java

package Thread.UncaughtExceptionHandler;

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

/**
 * 準備創建線程拋出異常,線程外捕獲
 * @author yxx
 *
 */
public class CaptureUncaughtException {

    public static void main(String[] args) {
        ExecutorService es = Executors.newCachedThreadPool(new HandlerThreadFactory());
        es.execute(new ExceptionThread2());
        es.execute(new ExceptionThread3());
    }
}

運行截圖

運行截圖

都能捕獲到

如果你知道將要在代碼中處處使用相同的異常處理器,那么更加簡單的方式是在Thread類中設置一個靜態域,并將這個處理器設置為默認的未捕獲異常的處理器。

在Thread類中設置一個靜態域

SettingDefaultHandler.java

package Thread.UncaughtExceptionHandler;

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

/**
 * 如果你知道將要在代碼中處處使用相同的異常處理器,那么更加簡單的方式是在Thread類中設置一個靜態域,
 * 并將這個處理器設置為默認的未捕獲異常的處理器。
 * @author yxx
 *
 */
public class SettingDefaultHandler {
    public static void main(String[] args) {
        Thread.setDefaultUncaughtExceptionHandler(new MyUncaughtExceptionHandler());

        ExecutorService es = Executors.newCachedThreadPool();
        es.execute(new ExceptionThread2());
    }
}

靜態域執行效果圖

靜態域執行效果圖

說明:這個處理器只有在不存在線程專有的未捕獲異常的情況下才會被調用。系統會檢查線程的專有的版本,如果沒有發現,則檢查線程組是有有其專有的uncaughtException()方法。如果也沒有,在調用defaultUncaughtExcept

在java多線程程序中,所有線程都不允許拋出未捕獲的checked exception(比如sleep時的InterruptedException),也就是說各個線程需要自己把自己的checked exception處理掉

這一點是通過java.lang.Runnable.run()方法聲明(因為此方法聲明上沒有throw exception部分)進行了約束。但是線程依然有可能拋出unchecked exception(如運行時異常),當此類異常跑拋出時,線程就會終結,而對于主線程和其他線程完全不受影響,且完全感知不到某個線程拋出的異常(也是說完全無法catch到這個異常)。JVM的這種設計源自于這樣一種理念:線程是獨立執行的代碼片斷,線程的問題應該由線程自己來解決,而不要委托到外部。”

“基于這樣的設計理念,在Java中,線程方法的異常(無論是checked還是unchecked exception),都應該在線程代碼邊界之內(run方法內)進行try catch并處理掉.換句話說,我們不能捕獲從線程中逃逸的異常。

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容

  • 兩類異常 Java中的異常繼承體系為: 這里的異常( Exception)分為兩大類:Checked異常和Runt...
    tobe_superman閱讀 656評論 0 0
  • 1. Java基礎部分 基礎部分的順序:基本語法,類相關的語法,內部類的語法,繼承相關的語法,異常的語法,線程的語...
    子非魚_t_閱讀 31,779評論 18 399
  • 六種異常處理的陋習 你覺得自己是一個Java專家嗎?是否肯定自己已經全面掌握了Java的異常處理機制?在下面這段代...
    Executing閱讀 1,367評論 0 6
  • (一)Java部分 1、列舉出JAVA中6個比較常用的包【天威誠信面試題】 【參考答案】 java.lang;ja...
    獨云閱讀 7,144評論 0 62
  • 我總是會不自覺的就在你面前說,你一定要好好學習,我為了你,放棄了我自己的成長,爸爸為了你,辛苦的工作。 我總是會把...
    一棵樹閱讀 228評論 0 0