介紹
在多線程中,主線程是無法捕獲到子線程的異常的。除非所有的異常能夠在run()
中被捕獲,否則將會把異常棧信息輸出到終端而中斷該線程。所以Thread提供了UncaughtExceptionHandler
來供我們在線程外部進行操作,確保子線程未捕獲的異常能夠被處理。
源碼分析
- Thread類中提供的接口
@FunctionalInterface
public interface UncaughtExceptionHandler {
void uncaughtException(Thread t, Throwable e);
}
- Thread類中提供的方法
private volatile UncaughtExceptionHandler uncaughtExceptionHandler;
//用於對特定的線程設置異常處理器。
public void setUncaughtExceptionHandler(UncaughtExceptionHandler eh) {
checkAccess();
uncaughtExceptionHandler = eh;
}
//用於獲取特定的線程的異常處理器。
public UncaughtExceptionHandler getUncaughtExceptionHandler() {
//若當前的線程沒有異常處理器,會使用所屬線程組的異常處理器
//詳見下面的ThreadGroup.uncaughtException
return uncaughtExceptionHandler != null ?
uncaughtExceptionHandler : group;
}
private static volatile UncaughtExceptionHandler defaultUncaughtExceptionHandler;
//靜態方法:用於設置一個默認的全局異常處理器
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;
}
- ThreadGroup.uncaughtException
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);
}
}
}
當發生異常時,它的調用順序是這樣:
- 查看是否有自己對象特有的 handler,如果有就直接處理
- 如果沒有,則調用所屬線程組的handler,這個過程會向上追溯父線程組的
uncaughtException
- 若還是沒有被處理,則使用全局的handler
- 這時候若沒有全局的handler,則直接執行正常的異常流程中斷該線程
Demo
先定義一個target,讓其會拋出ArithmeticException
public class RunnableImpl implements Runnable{
@Override
public void run() {
int i = 100/0;
}
}
定義異常處理器
//全局的異常處理器
public class GlobalExceptionHander implements Thread.UncaughtExceptionHandler {
@Override
public void uncaughtException(Thread t, Throwable e) {
System.out.println("線程"+t.getName()+"||全局的異常處理器||"+e.getMessage());
}
}
//特定線程的異常處理器
public class ExceptionHandler implements Thread.UncaughtExceptionHandler{
public void uncaughtException(Thread t, Throwable e) {
System.out.println("線程"+t.getName()+"||特定異常處理器||"+e.getMessage());
}
}
測試
public class ExceptionDemo {
public static void main(String[] args) throws InterruptedException {
//設置全局的異常處理器
Thread.setDefaultUncaughtExceptionHandler(new GlobalExceptionHander());
//線程一指定了自己的異常處理器
Thread thread1 = new Thread(new RunnableImpl(),"Thread1");
thread1.setUncaughtExceptionHandler(new ExceptionHandler());
thread1.start();
//線程二不使用特定的異常處理器
Thread thread2 = new Thread(new RunnableImpl(),"Thread2");
thread2.start();
}
}
結果
結果符合上述源碼分析的順序預期