线程逃逸
在Thread的run方法中,Java是不允许抛出受检异常的,所以必须由自己捕获,但是对于一些运行时的异常,难免有时候完全捕获到,继而传递到上一层,导致不可预料的程序终止,所以需要在上一层捕获
来看看我们到底不能捕获未受检异常
异常类
class MyRunnable implements Runnable {
private int ticket = 10000;
private Object object;
@Override
public void run() {
System.out.println(3/0);
System.out.println(3/2);
System.out.println(4/0);
}
}
main方法
public class ThreadDemo {
public static void main(String[] args) {
try{
MyRunnable runnable = new MyRunnable();
Thread t1 = new Thread(runnable, "one");
Thread t2 = new Thread(runnable, "two");
Thread t3 = new Thread(runnable, "third");
Thread t4 = new Thread(runnable, "four");
t1.start();
t2.start();
t3.start();
t4.start();
}catch (Exception e){
System.out.println("捕获了");
}
}
}
打印结果
可以看到,在main方法中,并没有捕获异常,导致异常的逃逸,造成意外的程序中断
UncaughtExceptionHandler
是为了捕获没有被捕获的异常,包括运行时异常,执行错误(内存溢出等),子线程抛出的异常等
每个线程都可以单独设置一个UncaughtExceptionHandler,在线程throw 了运行时异常的时候,异常被传递给未捕获异常处理器UncaughtExceptionHandler是Thread的一个内部接口,我们自定一个Handler
public class ThreadDemo {
public static void main(String[] args) {
MyRunnable runnable = new MyRunnable();
Thread t1 = new Thread(runnable, "one");
Thread t2 = new Thread(runnable, "two");
Thread t3 = new Thread(runnable, "third");
Thread t4 = new Thread(runnable, "four");
t1.setUncaughtExceptionHandler(new Thread.UncaughtExceptionHandler() {
@Override
public void uncaughtException(Thread t, Throwable e) {
System.out.println("捕获到了");
}
});
t1.start();
t2.start();
t3.start();
t4.start();
}
}
上面的代码中,t1这个线程的异常就可以会捕获到并处理
也可以设置Thread的默认UncaughtException,使用Thread的静态方法setDefaultUncaughtExceptionHandler
,这样所有的线程的抛出的异常都会被处理
MyRunnable runnable = new MyRunnable();
Thread t1 = new Thread(runnable, "one");
Thread t2 = new Thread(runnable, "two");
Thread t3 = new Thread(runnable, "third");
Thread t4 = new Thread(runnable, "four");
Thread.setDefaultUncaughtExceptionHandler(new Thread.UncaughtExceptionHandler() {
@Override
public void uncaughtException(Thread t, Throwable e) {
System.out.println("捕获到了线程 "+t.getName()+" 异常信息: "+e.getMessage());
}
});
t1.start();
t2.start();
t3.start();
t4.start();
输出结果,所有线程的异常都被捕获了
如果既没有设置单独的异常处理器,也没有设置默认的异常处理器,那么UncaughtException是ThreadGroup的处理器,ThreadGroup类实现了Thread.UncaughtExceptioin接口
ThreadGroup的调用规则
1.如果thread group有父group,那么执行父group的uncaughtException
2.否则如果线程Thread设置了默认的UncaughtExceptoin,那么调用默认的UncaughtExceptoin
3.否则如果异常不是ThreadDeath的实例(ThreadDeath对象是stop方法产生的),就打印出异常栈的信息到System.err流
请看源码
void uncaughtException(Thread t, Throwable e)
calls this method of the parent thread group if there is a parent, or calls the default
handler of the Thread class if there is a default handler, or otherwise prints a stack
trace to the standard error stream. (However, if e is a ThreadDeath object, the stack trace
is suppressed. ThreadDeath objects are generated by the deprecated stopmethod.)
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的使用,见我另一篇博客