UncaughtException
主线程可以轻松发现异常,子线程不行
由于子线程无法像主线程那样在未捕获异常出现时,终止卡主,轻松排查.同时作为一个兜底的功能,避免不必要的信息提交到前端
public static void main(String[] args) throws InterruptedException {
new Thread(()->{
System.out.println(1/0);
}).start();
Thread.sleep(100);
System.out.println("test");
}
Exception in thread "Thread-0" java.lang.ArithmeticException: / by zero
at com.example.threadCoreKnowledge.exception.ThreadException.lambda$main$0(ThreadException.java:6)
at java.lang.Thread.run(Thread.java:748)
test
子线程异常无法用传统方式捕获
public static void main(String[] args) throws InterruptedException {
try {
new Thread(()->{
System.out.println(1/0);
}).start();
} catch (Exception e) {
System.out.println("catch");
System.out.println(e);
}
Thread.sleep(100);
System.out.println("main");
}
Exception in thread "Thread-0" java.lang.ArithmeticException: / by zero
at com.example.threadCoreKnowledge.exception.CantCatchException.lambda$main$0(CantCatchException.java:7)
at java.lang.Thread.run(Thread.java:748)
main
可以看到,虽然通过try-catch包裹,但是无效.
这是因为try-catch只能捕获当前线程的异常,也就是如果start()语句报错,可以捕获.
但**System.out.println(1/0)**已经属于新的线程执行的语句了,所以无法捕获.
不直接捕获的后果.直接捕获带来的健壮性的提升
未直接捕获,异常堆栈打印后就结束了,无法对异常做自定义的处理,导致异常出现后,无法即时处理.且因为程序运行时都会调用大量的jar包的方法,异常信息如果抛出给jar包的方法处理,可能会被直接吞掉,哪怕没吞掉也会有大量无用信息,增大排查难度
直接捕获后,由于可以自定义异常处理逻辑,这样通过如短信通知,邮件通知等方式,可以快速判断是否需要人工介入.异常的信息也可以做格式处理.
解决方法
在每个run方法中手动try-catch
这样的麻烦就是每个线程都需要编写可能一样的处理逻辑
通过UncaughtExceptionHandler全局处理
运行的代码
public static void main(String[] args) {
new Thread(()-> System.out.println(1/0)).start();
}
默认的处理逻辑
//java.lang.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);
}
}
}
可以看到这里就是默认处理的逻辑,且如果parent为空,就会通过获取默认Thread.getDefaultUncaughtExceptionHandler()的方式来处理,如果还未空,就直接打印堆栈信息.
设置全局默认
public static void main(String[] args) {
Thread.setDefaultUncaughtExceptionHandler((t,e)->{
System.out.println(t);
System.out.println(e);
});
Thread thread = new Thread(() -> System.out.println(1 / 0));
Thread thread1 = new Thread(() -> {throw new NullPointerException();});
thread.start();thread1.start();
}
Thread[Thread-0,5,main]
java.lang.ArithmeticException: / by zero
Thread[Thread-1,5,main]
java.lang.NullPointerException
单个设置
public static void main(String[] args) {
Thread thread = new Thread(() -> {
Thread.currentThread().setUncaughtExceptionHandler((t, e) -> {
System.out.print(e);
System.out.println("自定义设置1");
});
System.out.println(1 / 0);
});
Thread thread1 = new Thread(() -> {
Thread.currentThread().setUncaughtExceptionHandler((t, e) -> {
System.out.print(e);
System.out.println("自定义设置2");
});
throw new NullPointerException();
});
thread.start();
thread1.start();
}
java.lang.NullPointerException自定义设置2
java.lang.ArithmeticException: / by zero自定义设置1
线程池设置
public static void main(String[] args) {
ThreadPoolExecutor executor = new ThreadPoolExecutor(4, 10,
120L, TimeUnit.SECONDS, new LinkedBlockingQueue<>(),
new CustomThreadFactory());
executor.execute(()-> System.out.println(1/0));
}
static class CustomThreadFactory implements ThreadFactory{
@Override
public Thread newThread(Runnable r) {
Thread t = new Thread(r);
t.setUncaughtExceptionHandler((th,e)->{
System.out.println("threadPoolCustomExceptionHandler");
System.out.println(e);
});
return t;
}
}
threadPoolCustomExceptionHandler
java.lang.ArithmeticException: / by zero