1. 程式人生 > >UncaughtExceptionHandler處理執行緒中的執行時異常

UncaughtExceptionHandler處理執行緒中的執行時異常

執行緒在執行單元中不允許丟擲checked異常,而且執行緒執行在自己的上下文中,派生它的執行緒無法直接獲得它執行中出現的異常資訊。對此,Java為我們提供了UncaughtExceptionHandler介面,當執行緒在執行過程中出現異常時,會回撥UncaughtExceptionHandler介面,從而得知是哪個執行緒在執行時出錯。UncaughtExceptionHandler介面在Thread中定義。

Thread類中,關於處理執行時異常的API有四個:

  • public void setUncaughtExceptionHandler(UncaughtExceptionHandler eh)

    為某個特定執行緒指定UncaughtExceptionHandler
  • public static void setDefaultUncaughtExceptionHandler(UncaughtExceptionHandler eh)
    設定全域性的UncaughtExceptionHandler
  • public UncaughtExceptionHandler getUncaughtExceptionHandler()
    獲取特定執行緒的UncaughtExceptionHandler
  • public static UncaughtExceptionHandler getDefaultUncaughtExceptionHandler()

    獲取全域性的UncaughtExceptionHandler

UncaughtExceptionHandler 簡單使用

// 全域性異常處理
Thread.setDefaultUncaughtExceptionHandler(new Thread.UncaughtExceptionHandler() {
	public void uncaughtException(Thread t, Throwable e) {
		System.out.printf("Default , Thread name : %s , Exception : %s\n", t.getName(), e.getClass().getSimpleName());
		e.printStackTrace();

	}
});
Thread arithmetic = new Thread(() -> {
	throw new ArithmeticException();
}, "arithmetic");
arithmetic.start();
// ----------------------------------------------
Thread nullPoint = new Thread(() -> {
	throw new NullPointerException();
}, "nullPoint");
// 指定異常處理
nullPoint.setUncaughtExceptionHandler(new Thread.UncaughtExceptionHandler() {
	public void uncaughtException(Thread t, Throwable e) {
		System.out.printf("Assign , Thread name : %s , Exception : %s\n", t.getName(), e.getClass().getSimpleName());
		e.printStackTrace();
	}
});
nullPoint.start();

輸出結果:

Default , Thread name : arithmetic , Exception : ArithmeticException
Assign , Thread name : nullPoint , Exception : NullPointerException
java.lang.ArithmeticException
	at com.p7.demo.UncaughtExceptionHandlerTest.lambda$0(UncaughtExceptionHandlerTest.java:24)
	at java.lang.Thread.run(Thread.java:748)
java.lang.NullPointerException
	at com.p7.demo.UncaughtExceptionHandlerTest.lambda$1(UncaughtExceptionHandlerTest.java:32)
	at java.lang.Thread.run(Thread.java:748)

UncaughtExceptionHandler 原始碼分析

/* The group of this thread,每建立一個Thread物件時,都會呼叫Thread的init方法,這個方法初始化了當前執行緒的執行緒組 */
private ThreadGroup group;
public UncaughtExceptionHandler getUncaughtExceptionHandler() {
    return uncaughtExceptionHandler != null ?
        uncaughtExceptionHandler : group;
}

getUncaughtExceptionHandler方法首先判斷當前執行緒是否設定了handler,如果有則使用自己的uncaughtException方法,否則就在所屬的ThreadGroup中獲取,ThreadGroup實現了UncaughtExceptionHandler

private final ThreadGroup parent;
public void uncaughtException(Thread t, Throwable e) {
    if (parent != null) {
        parent.uncaughtException(t, e);
    } else {
        Thread.UncaughtExceptionHandler ueh =
            Thread.getDefaultUncaughtExceptionHandler();
        if (ueh != null) {
            ueh.uncaughtException(t, e);
        } else if (!(e instanceof ThreadDeath)) {
            System.err.print("Exception in thread \""
                             + t.getName() + "\" ");
            e.printStackTrace(System.err);
        }
    }
}

ThreadGroup如果有父ThreadGroup,則直接呼叫父GroupuncaughtException;如果設定了全域性預設的UncaughtExceptionHandler,呼叫全域性的uncaughtException;如果沒有父ThreadGroup且沒有全域性預設的UncaughtExceptionHandler,直接將異常的堆疊資訊定向到System.err中。