线程的未捕获异常UncaughtException应该如何处理
为什么需要UncaughExceptionHandler
主线程可以轻松发现异常,子线程却不行
public class ExceptionInChildThread implements Runnable {
public static void main(String[] args) {
new Thread(new ExceptionInChildThread()).start();
for (int i = 0; i < 1000; i++) {
System.out.println(i);
}
}
@Override
public void run() {
throw new RuntimeException();
}
}
在这段代码的执行输出中,子线程确实抛出了异常,主线程也是不受影响。但是随着主线程的打印数据过多,开发人员可能会忽略掉(没有发现)这个子线程的异常。
子线程异常无法用传统方法捕获
不加try/catch的情况下
public class CantCatchDirectly implements Runnable {
public static void main(String[] args) throws InterruptedException {
new Thread(new CantCatchDirectly(), "线程-1").start();
Thread.sleep(300);
new Thread(new CantCatchDirectly(), "线程-2").start();
Thread.sleep(300);
new Thread(new CantCatchDirectly(), "线程-3").start();
Thread.sleep(300);
new Thread(new CantCatchDirectly(), "线程-4").start();
}
@Override
public void run() {
throw new RuntimeException();
}
}
从输出中,4个线程确实抛出了异常
Exception in thread "线程-1" java.lang.RuntimeException
at threadcoreknowledge.ubcaughtexception.CantCatchDirectly.run(CantCatchDirectly.java:27)
at java.lang.Thread.run(Thread.java:748)
Exception in thread "线程-2" java.lang.RuntimeException
at threadcoreknowledge.ubcaughtexception.CantCatchDirectly.run(CantCatchDirectly.java:27)
at java.lang.Thread.run(Thread.java:748)
Exception in thread "线程-3" java.lang.RuntimeException
at threadcoreknowledge.ubcaughtexception.CantCatchDirectly.run(CantCatchDirectly.java:27)
at java.lang.Thread.run(Thread.java:748)
Exception in thread "线程-4" java.lang.RuntimeException
at threadcoreknowledge.ubcaughtexception.CantCatchDirectly.run(CantCatchDirectly.java:27)
at java.lang.Thread.run(Thread.java:748)
加了try/catch的情况下
public class CantCatchDirectly implements Runnable {
public static void main(String[] args) throws InterruptedException {
try {
new Thread(new CantCatchDirectly(), "线程-1").start();
Thread.sleep(300);
new Thread(new CantCatchDirectly(), "线程-2").start();
Thread.sleep(300);
new Thread(new CantCatchDirectly(), "线程-3").start();
Thread.sleep(300);
new Thread(new CantCatchDirectly(), "线程-4").start();
} catch (RuntimeException e) {
System.out.println("抓住了异常");
}
}
@Override
public void run() {
throw new RuntimeException();
}
}
从输出中,并没有打印“抓住了异常”,那就是并没有被catch,这是为什么呢?
这是由于try/catch的部分是对于主线的,只有在启动线程的时候发生异常才会被catch,而这里的异常是在子线程中抛出的,所以try/catch没有捕获到。
Exception in thread "线程-1" java.lang.RuntimeException
at threadcoreknowledge.ubcaughtexception.CantCatchDirectly.run(CantCatchDirectly.java:31)
at java.lang.Thread.run(Thread.java:748)
Exception in thread "线程-2" java.lang.RuntimeException
at threadcoreknowledge.ubcaughtexception.CantCatchDirectly.run(CantCatchDirectly.java:31)
at java.lang.Thread.run(Thread.java:748)
Exception in thread "线程-3" java.lang.RuntimeException
at threadcoreknowledge.ubcaughtexception.CantCatchDirectly.run(CantCatchDirectly.java:31)
at java.lang.Thread.run(Thread.java:748)
Exception in thread "线程-4" java.lang.RuntimeException
at threadcoreknowledge.ubcaughtexception.CantCatchDirectly.run(CantCatchDirectly.java:31)
at java.lang.Thread.run(Thread.java:748)
不能直接捕获的后果、提高健壮性
对于子线程直接抛出异常,不进行捕获,那么他就是打印堆栈,子线程就是停止,那就没有人去查看我们的业务逻辑到底是出现了什么问题,所以需要用一个全局的Handler来进行处理(比如线程重启,或者通知开发者来介入查看)。
解决方案
手动在每个run方法里进行try/catch(不推荐)
在之前的代码基础上,在run方法中加入try/catch
public class CantCatchDirectly implements Runnable {
public static void main(String[] args) throws InterruptedException {
try {
new Thread(new CantCatchDirectly(), "线程-1").start();
Thread.sleep(300);
new Thread(new CantCatchDirectly(), "线程-2").start();
Thread.sleep(300);
new Thread(new CantCatchDirectly(), "线程-3").start();
Thread.sleep(300);
new Thread(new CantCatchDirectly(), "线程-4").start();
} catch (RuntimeException e) {
System.out.println("抓住了异常");
}
}
@Override
public void run() {
try {
throw new RuntimeException();
} catch (RuntimeException e) {
System.out.println("抓住了异常");
}
}
}
确实抓出了异常,但是这样我们要为每一个类进行try/catch,而且有时候我们并不知道他异常的类是什么,这样就过于麻烦。
抓住了异常
抓住了异常
抓住了异常
抓住了异常
利用UncaughtExceptionHandler
UncaughtExceptionHandler接口代码
@FunctionalInterface
public interface UncaughtExceptionHandler {
/**
* Method invoked when the given thread terminates due to the
* given uncaught exception.
* <p>Any exception thrown by this method will be ignored by the
* Java Virtual Machine.
* @param t the thread
* @param e the exception
*/
void uncaughtException(Thread t, Throwable e);
}
异常处理器的调用策略
首先会检查有没有父线程,如果有的话就用父线程的方法,他采用的是递归的方法逐级去找,一直找到最顶层的ThreadGroup的处理器;如果没有则getDefaultUncaughtExceptionHandler(),如果取到不为空,就可以用我们实现的异常处理去处理,反之就输出异常堆栈。
这里就是需要我们自己去实现一个异常处理。
private final ThreadGroup parent;
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);
}
}
}
实现异常处理器
处理器实现
public class MyUncaughtExceptionHandler implements Thread.UncaughtExceptionHandler {
private String name;
public MyUncaughtExceptionHandler(String name) {
this.name = name;
}
@Override
public void uncaughtException(Thread t, Throwable e) {
Logger logger = Logger.getAnonymousLogger();
logger.log(Level.WARNING, "线程异常,终止啦" + t.getName(), e);
System.out.println(name + "捕获了异常" + t.getName() + "异常" + e);
}
}
处理器调用
public class UseOwnUncaughtExceptionHandler implements Runnable {
public static void main(String[] args) throws InterruptedException {
Thread.setDefaultUncaughtExceptionHandler(new MyUncaughtExceptionHandler("捕获器"));
new Thread(new UseOwnUncaughtExceptionHandler(), "线程-1").start();
Thread.sleep(300);
new Thread(new UseOwnUncaughtExceptionHandler(), "线程-2").start();
Thread.sleep(300);
new Thread(new UseOwnUncaughtExceptionHandler(), "线程-3").start();
Thread.sleep(300);
new Thread(new UseOwnUncaughtExceptionHandler(), "线程-4").start();
}
@Override
public void run() {
throw new RuntimeException();
}
}
输出
五月 24, 2020 7:42:17 下午 threadcoreknowledge.ubcaughtexception.MyUncaughtExceptionHandler uncaughtException
警告: 线程异常,终止啦线程-1
java.lang.RuntimeException
at threadcoreknowledge.ubcaughtexception.UseOwnUncaughtExceptionHandler.run(UseOwnUncaughtExceptionHandler.java:24)
at java.lang.Thread.run(Thread.java:748)
捕获器捕获了异常线程-1异常java.lang.RuntimeException
五月 24, 2020 7:42:17 下午 threadcoreknowledge.ubcaughtexception.MyUncaughtExceptionHandler uncaughtException
警告: 线程异常,终止啦线程-2
java.lang.RuntimeException
at threadcoreknowledge.ubcaughtexception.UseOwnUncaughtExceptionHandler.run(UseOwnUncaughtExceptionHandler.java:24)
at java.lang.Thread.run(Thread.java:748)
捕获器捕获了异常线程-2异常java.lang.RuntimeException
五月 24, 2020 7:42:18 下午 threadcoreknowledge.ubcaughtexception.MyUncaughtExceptionHandler uncaughtException
警告: 线程异常,终止啦线程-3
java.lang.RuntimeException
at threadcoreknowledge.ubcaughtexception.UseOwnUncaughtExceptionHandler.run(UseOwnUncaughtExceptionHandler.java:24)
at java.lang.Thread.run(Thread.java:748)
捕获器捕获了异常线程-3异常java.lang.RuntimeException
五月 24, 2020 7:42:18 下午 threadcoreknowledge.ubcaughtexception.MyUncaughtExceptionHandler uncaughtException
警告: 线程异常,终止啦线程-4
java.lang.RuntimeException
at threadcoreknowledge.ubcaughtexception.UseOwnUncaughtExceptionHandler.run(UseOwnUncaughtExceptionHandler.java:24)
at java.lang.Thread.run(Thread.java:748)
捕获器捕获了异常线程-4异常java.lang.RuntimeException