零 背景描述
Java的异常在线程之间不是共享的,在线程中抛出的异常是线程自己的异常,主线程并不能捕获到。也就是说你把线程执行的代码看成另一个主函数:
public static void main(String... args) {
//A
new Thread(() -> {
//B
}).start();
}
上面A和B的运行是互相独立的,虽然说你看到B所在代码块的函数内容在main中,但是main并不能捕获到这个Runnable里函数的异常,因为它不在同一个线程之中运行,B中抛出的异常如果你不在另一个线程捕获的话,相当于就是没有异常处理,无法捕获。你这里的代码使用的是RuntimeException,你可以试试使用必须捕获的异常,编译器会报错,因为你在另一个线程中没有做任何异常处理。
那么我们如何对异步线程出现的异常进行处理呢?
一 对于单独线程的异常捕捉
在Thread中,Java提供了一个setUncaughtExceptionHandler
的方法来设置线程
的异常处理函数,你可以把异常处理函数传进去,当发生线程的未捕获异常的时候,由JVM来回调执行。
例子如下:
1.写个异常,int肯定格式化转换失败抛 java.lang.NumberFormatException:异常
public class ThreadExceptionRun implements Runnable {
@Override
public void run() {
int i = Integer.parseInt("uncaughtException");
System.out.println(i);
}
}
2.写个异常处理器,实现Thread.UncaughtExceptionHandler重写uncaughtException方法
public class ThreadException implements Thread.UncaughtExceptionHandler {
@Override
public void uncaughtException(Thread t, Throwable e) {
System.out.println("处理异常信息");
}
}
3.使用
public class ThreadExceptionMain {
public static void main(String[] args) {
Thread thread = new Thread(new ThreadExceptionRun());
thread.setUncaughtExceptionHandler(new ThreadException());
thread.start();
}
}
二 对于线程池如何进行异步线程异常捕捉?
下面给线程池对于不可捕捉异常也提供了多种方式去处理:
1. run方法里面try/catch所有处理逻辑
public void run() {
try {
//处理逻辑
} catch(Exeception e) {
//打印日志
}
}
这是一种简单而且不易出错的线程池异常处理方式,推荐使用
2.重写ThreadPoolExecutor.afterExecute方法
前面分析过,线程池的线程在执行结束前肯定调用afterExecute方法,所有只需要重写该方法即可。
public class MyThreadPool extends ThreadPoolExecutor {
public MyThreadPool(int corePoolSize, int maximumPoolSize,
long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue) {
super(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue);
}
@Override
public void afterExecute(Runnable r, Throwable t) {
if(t != null) {
System.out.println("打印异常日志:" + t);
}
}
}
3. 使用submit执行任务
我们知道在使用submit执行任务,该方法将返回一个Future对象,不仅仅是任务的执行结果,异常也会被封装到Future对象中,通过get()方法获取。
关于这块详情可以看 https://blog.csdn.net/LJJZJ/article/details/102905926