问:说说你对 UncaughtExceptionHandler 的认识和理解?
答:在虚拟机中,当一个线程如果没有显式处理(即 try catch
)异常而抛出时会将该异常事件报告给该线程对象的 java.lang.Thread.UncaughtExceptionHandler
进行处理,如果线程没有设置 UncaughtExceptionHandler
,则默认会把异常栈信息输出到终端而使程序直接崩溃。所以如果我们想在线程意外崩溃时做一些处理就可以通过实现 UncaughtExceptionHandler
来满足需求。
为了让大家搞清其中接口方法的细节差异,下面给出核心源码解释(有删减):
public class Thread {
......
/**
* 当一个线程因未捕获的异常而即将终止时虚拟机将使用 Thread.getUncaughtExceptionHandler()
* 获取已经设置的 UncaughtExceptionHandler 实例,并通过调用其 uncaughtException(...) 方
* 法而传递相关异常信息。
* 如果一个线程没有明确设置其 UncaughtExceptionHandler,则将其 ThreadGroup 对象作为其
* handler,如果 ThreadGroup 对象对异常没有什么特殊的要求,则 ThreadGroup 会将调用转发给
* 默认的未捕获异常处理器(即 Thread 类中定义的静态未捕获异常处理器对象)。
*
* @see #setDefaultUncaughtExceptionHandler
* @see #setUncaughtExceptionHandler
* @see ThreadGroup#uncaughtException
*/
@FunctionalInterface
public interface UncaughtExceptionHandler {
/**
* 未捕获异常崩溃时回调此方法
*/
void uncaughtException(Thread t, Throwable e);
}
/**
* 静态方法,用于设置一个默认的全局异常处理器。
*/
public static void setDefaultUncaughtExceptionHandler(UncaughtExceptionHandler eh) {
defaultUncaughtExceptionHandler = eh;
}
/**
* 针对某个 Thread 对象的方法,用于对特定的线程进行未捕获的异常处理。
*/
public void setUncaughtExceptionHandler(UncaughtExceptionHandler eh) {
checkAccess();
uncaughtExceptionHandler = eh;
}
/**
* 当 Thread 崩溃时会调用该方法获取当前线程的 handler,获取不到就会调用 group(handler 类型)。
* group 是 Thread 类的 ThreadGroup 类型属性,在 Thread 构造中实例化。
*/
public UncaughtExceptionHandler getUncaughtExceptionHandler() {
return uncaughtExceptionHandler != null ?
uncaughtExceptionHandler : group;
}
/**
* 线程全局默认 handler。
*/
public static UncaughtExceptionHandler getDefaultUncaughtExceptionHandler() {
return defaultUncaughtExceptionHandler;
}
......
}
通过上面源码可以看出,线程崩溃时异常抛出的顺序是先调用 Thread
的 getUncaughtExceptionHandler()
查看是否有自己对象特有的 handler
,如果有就直接处理,如果没有就调用 ThreadGroup
(UncaughtExceptionHandler
在 JDK 的默认实现类),如果 ThreadGroup
没有特殊处理就会继续调用 Thread
的 getDefaultUncaughtExceptionHandler()
获取 handler
进行处理,如果默认 handler
也没有处理就直接执行正常的异常流程使程序崩溃。
下面看下 ThreadGroup
的核心实现源码,如下:
//ThreadGroup 在 Thread 对象构造方法中实例化。
public class ThreadGroup implements Thread.UncaughtExceptionHandler {
......
public void uncaughtException(Thread t, Throwable e) {
//默认情况下 parent 是 null。
if (parent != null) {
parent.uncaughtException(t, e);
} else {
//一般走进来,调用 Thread.setDefaultUncaughtExceptionHandler(...)
//方法设置的全局 handler 进行处理。
Thread.UncaughtExceptionHandler ueh =
Thread.getDefaultUncaughtExceptionHandler();
if (ueh != null) {
ueh.uncaughtException(t, e);
} else if (!(e instanceof ThreadDeath)) {
//全局 handler 也不存在就输出异常栈。
System.err.print("Exception in thread \""
+ t.getName() + "\" ");
e.printStackTrace(System.err);
}
}
}
......
}