當一個執行緒在執行過程中丟擲了異常,並且沒有進行try..catch,那麼這個執行緒就會終止執行。
在Thread類中,提供了兩個可以設定執行緒未捕獲異常的全域性處理器,我們可以在處理器裡做一些工作,例如將異常資訊傳送到遠端伺服器。
雖然這可以捕獲到執行緒中的異常,但是並不能阻止執行緒停止執行。因此該線上程run方法裡try..catch的,還是要好好的進行try..catch。
從Thread類原始碼中可以看到這2個變數:
private volatile UncaughtExceptionHandler uncaughtExceptionHandler;
private static volatile UncaughtExceptionHandler defaultUncaughtExceptionHandler;
需要注意到區別,defaultUncaughtExceptionHandler是靜態的,我們可以呼叫此方法設定所有執行緒物件的例外處理器,而uncaughtExceptionHandler則是針對單個執行緒物件的例外處理器。
uncaughtExceptionHandler優先順序高於defaultUncaughtExceptionHandler。
Thread類提供了這2個變數的setter/getter:
public static void setDefaultUncaughtExceptionHandler(UncaughtExceptionHandler eh) {
SecurityManager sm = System.getSecurityManager();
if (sm != null) {
sm.checkPermission(
new RuntimePermission("setDefaultUncaughtExceptionHandler")
);
}
defaultUncaughtExceptionHandler = eh;
}
public static UncaughtExceptionHandler getDefaultUncaughtExceptionHandler(){
return defaultUncaughtExceptionHandler;
}
public UncaughtExceptionHandler getUncaughtExceptionHandler() {
return uncaughtExceptionHandler != null ?
uncaughtExceptionHandler : group;
}
public void setUncaughtExceptionHandler(UncaughtExceptionHandler eh) {
checkAccess();
uncaughtExceptionHandler = eh;
}
可以看到,getUncaughtExceptionHandler()中進行了判斷,當uncaughtExceptionHandler為null時返回group。
我們來看下UncaughtExceptionHandler介面是怎麼宣告的:
@FunctionalInterface
public interface UncaughtExceptionHandler {
void uncaughtException(Thread t, Throwable e);
}
我們只需要實現UncaughtExceptionHandler介面,重寫uncaughtException方法即可進行例外處理。
那麼JVM是怎麼檢測到執行緒發生異常,並將異常分發到處理器的呢?
對於這塊程式碼,JDK原始碼中看不到是如何處理的,可能需要翻閱hotspot原始碼,不過Thread類中提供了一個dispatchUncaughtException方法,將異常回撥到了uncaughtExceptionHandler中去處理。
private void dispatchUncaughtException(Throwable e) {
getUncaughtExceptionHandler().uncaughtException(this, e);
}
很明顯,dispatchUncaughtException應該就是提供給hotspot進行JNI回撥的。
而對於defaultUncaughtExceptionHandler的呼叫,猜測應該是在hotspot中直接完成了。
接下來我們用範例來演示一下例外處理器的效果。
範例:
Thread thread = new Thread(() -> {
System.out.println("run before");
System.out.println("runing");
if(1 == 1) {
throw new IllegalStateException("exception");
}
System.out.println("run after");
});
thread.setUncaughtExceptionHandler((t, e) -> System.out.println("捕獲異常," + t.getName() + "," + e.getMessage()));
Thread.setDefaultUncaughtExceptionHandler((t, e) -> System.out.println("Default捕獲異常," + t.getName() + "," + e.getMessage()));
thread.start();
輸出:
run before
runing
捕獲異常,Thread-0,exception
可以看出,雖然兩個例外處理器都有設定,並且defaultUncaughtExceptionHandler是最後設定的,不過起效的是uncaughtExceptionHandler。
可以將thread.setUncaughtExceptionHandler(...);註釋掉:
輸出:
run before
runing
Default捕獲異常,Thread-0,exception
註釋後,defaultUncaughtExceptionHandler起效了,證明了uncaughtExceptionHandler優先順序高於defaultUncaughtExceptionHandler。