Java 线程处理异常
线程出现未捕获异常后,JVM将调用Thread中的dispatchUncaughtException方法把异常传递给线程的未捕获异常处理器。
private void dispatchUncaughtException(Throwable e) {
getUncaughtExceptionHandler().uncaughtException(this, e);
}
public UncaughtExceptionHandler getUncaughtExceptionHandler() {
return uncaughtExceptionHandler != null ?
uncaughtExceptionHandler : group;
}
复制代码
Thread中存在两个UncaughtExceptionHandler。一个是静态的defaultUncaughtExceptionHandler,另一个是非静态uncaughtExceptionHandler。
// null unless explicitly set
private volatile UncaughtExceptionHandler uncaughtExceptionHandler;
// null unless explicitly set
private static volatile UncaughtExceptionHandler defaultUncaughtExceptionHandler;
复制代码
- defaultUncaughtExceptionHandler:设置一个静态的默认的UncaughtExceptionHandler。来自所有线程中的Exception在抛出并且未捕获的情况下,都会从此路过。进程fork的时候设置的就是这个静态的defaultUncaughtExceptionHandler,管辖范围为整个进程。
- uncaughtExceptionHandler:为单个线程设置一个属于线程自己的uncaughtExceptionHandler,辖范围比较小。
如果没有设置uncaughtExceptionHandler,将使用线程所在的线程组来处理这个未捕获异常。线程组ThreadGroup实现了UncaughtExceptionHandler,所以可以用来处理未捕获异常。ThreadGroup类定义:
private ThreadGroup group;
class ThreadGroup implements Thread.UncaughtExceptionHandler{}
复制代码
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);
}
}
}
复制代码
默认情况下,线程组处理未捕获异常的逻辑是,首先将异常消息通知给父线程组,然后尝试利用一个默认的defaultUncaughtExceptionHandler来处理异常,如果没有默认的异常处理器则将错误信息输出到System.err。也就是JVM提供给我们设置每个线程的具体的未捕获异常处理器,也提供了设置默认异常处理器的方法。
Android 线程处理异常
在Android平台中,应用进程fork出来后会为虚拟机设置一个未截获异常处理器, 即在程序运行时,如果有任何一个线程抛出了未被截获的异常, 那么该异常最终会抛给未截获异常处理器处理。
Thread.setDefaultUncaughtExceptionHandler(new UncaughtHandler());
复制代码
UncaughtHandler
public interface UncaughtExceptionHandler {
void uncaughtException(Thread t, Throwable e);
}
复制代码
private static class UncaughtHandler implements Thread.UncaughtExceptionHandler {
public void uncaughtException(Thread t, Throwable e) {
try {
// Don't re-enter -- avoid infinite loops if crash-reporting crashes.
if (mCrashing) return;
mCrashing = true;
if (mApplicationObject == null) {
Clog_e(TAG, "*** FATAL EXCEPTION IN SYSTEM PROCESS: " + t.getName(), e);
} else {
//打印进程的crash信息
.............
}
.............
// Bring up crash dialog, wait for it to be dismissed
//调用AMS的接口,进行处理 ANR之类
ActivityManagerNative.getDefault().handleApplicationCrash(
mApplicationObject, new ApplicationErrorReport.CrashInfo(e));
} catch (Throwable t2) {
if (t2 instanceof DeadObjectException) {
// System process is dead; ignore
} else {
try {
Clog_e(TAG, "Error reporting crash", t2);
} catch (Throwable t3) {
// Even Clog_e() fails! Oh well.
}
}
} finally {
//crash的最后,会杀死进程
Process.killProcess(Process.myPid());
//并exit
System.exit(10);
}
}
}
复制代码
UncaughtExceptionHandler存在于Thread中.当异常发生且未捕获时。异常会透过UncaughtExceptionHandler抛出。并且该线程会消亡。所以在Android中子线程死亡是允许的。主线程死亡就会导致ANR.
自定义 Thread.UncaughtExceptionHandler
设置了默认的异常处理器后,系统中所有未直接设置异常处理器的线程将使用这个默认的异常处理器。
public void defaultWay(){
Thread.setDefaultUncaughtExceptionHandler(new Thread.UncaughtExceptionHandler() {
@Override
public void uncaughtException(Thread t, Throwable e) {
System.out.println("I catch a exception from " + Thread.currentThread().getName() + ":" + Thread.currentThread().getThreadGroup().getName());
}
});
ThreadGroup myGroup = new ThreadGroup("myGroup");
new Thread(myGroup, new Runnable() {
@Override
public void run() {
int i = 1/0;
}
}, "thread1").start();
new Thread(myGroup, new Runnable() {
@Override
public void run() {
int i = 1/0;
}
}, "thread2").start();
}
复制代码
这段代码创建了两个线程,并且它们都会抛出异常,最终由统一的默认异常处理器来处理。结果:
I catch a exception from thread1:myGroup
I catch a exception from thread2:myGroup
复制代码
当然,出现上面的结果是因为使用了默认的ThreadGroup,我们可以破坏它这个机制。如果把上面代码中的ThreadGroup换成下面的BadGroup则情况会发生变化:
class BadGroup extends ThreadGroup{
public BadGroup(String name) {
super(name);
}
@Override
public void uncaughtException(Thread t, Throwable e) {
System.out.println("I am a bad group and do nothing");
}
}
复制代码
如果使用了BadGroup得出结果将是打印两条I am a bad group and do nothing。