线程出现异常
线程执行任务时,没有添加异常处理。导致任务内部发生异常时,内部错误无法被记录下来。
Thread thread =new Thread(()->{
if (1 == 1) {
log.error("error");
throw new RuntimeException("异常了");
}
});
thread.start();
}
可以看到,异常不会打印日志,而是输出在控制台上了。那么异常去了哪里?
异常去了哪里?
正常来说,如果我们进行了异常捕获,是可以看到日志信息的。
Thread thread = new Thread(()->{
try{
log.info("hello");
throw new RuntimeException("运行时异常了");
}catch (Exception e){
log.error("异常发生",e);
}
});
thread.start();
但是如果是一个未捕获的异常,异常是抛到控制台上的,什么原因呢?
如果一个异常未被捕获,从线程中抛了出来。JVM会回调T hread 类中dispatchUncaughtException方法。
在这方法中,就是获取一个默认的异常的处理器,我们来看看这个处理器它是怎么做的?
可以看到这里,他不会记录日志信息,而是将异常抛到了控制台中,那么线上我们一般是通过日志来排查问题,所以,我们怎么将控制台的输出转变成日志输出呢?
解决方案
其实,很简单,就是给线程添加一个异常捕获处理器,以后抛了异常,就给它转成日志。这样才能及时发现问题。
首先定义一个异常处理器:
/**
* 自定义异常处理器(将异常时控制台输出,装变成日志输出)
*/
@Slf4j
public class MyUncaughtExceptionHandler implements Thread.UncaughtExceptionHandler {
@Override
public void uncaughtException(Thread t, Throwable e) {
log.error("Exception Thread",e);
}
}
设置异常处理器
Thread thread =new Thread(()->{
if (1 == 1) {
log.error("error");
throw new RuntimeException("异常了");
}
});
// 设置异常处理器
thread.setUncaughtExceptionHandler(new MyUncaughtExceptionHandler());
thread.start();
可以看到即使没有获取异常,抛出的异常,我们也记录在日志中。
线程池如何进行异常处理呢?
可以通过线程池的ThreadFactory,创建线程的工厂,创建线程的时候给线程添加异常捕获。
首先自定义线程工厂,然后就是在保持原有线程工厂的能力,然后额外的配置我们自己自定义的异常处理器。
/**
* 自定义线程工厂(提供处理异常的能力)
*/
public class MyThreadFactory implements ThreadFactory {
private static final MyUncaughtExceptionHandler UNCAUGHT_EXCEPTION_HANDLER = new MyUncaughtExceptionHandler();
private ThreadFactory originalThreadFactory;
public MyThreadFactory(ThreadFactory originalThreadFactory) {
this.originalThreadFactory = originalThreadFactory;
}
@Override
public Thread newThread(Runnable r) {
Thread thread = originalThreadFactory.newThread(r);
// 设置我们自定义的异常处理器
thread.setUncaughtExceptionHandler(UNCAUGHT_EXCEPTION_HANDLER);
return thread;
}
}
在线程池中配置我们的线程池工厂:
这里我使用的是Spring提供的线程池。
@Configuration
public class ThreadPoolConfig {
@Bean
public ThreadPoolTaskExecutor taskExecutor() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.setCorePoolSize(5);
executor.setMaxPoolSize(10);
executor.setQueueCapacity(100);
executor.setThreadNamePrefix("MyExecutor-");
executor.setThreadFactory(new MyThreadFactory(executor));
executor.initialize();
return executor;
}
}
new MyThreadFactory() 这里为什么可以传入这个线程池类,因为Spring中这个类本身就是一个线程工厂的类。
可以从下面看到 ThreadPoolTaskExecutor 继承了 ExecutorConfigurationSupport
可以从关系图中看到是实现了 ThreadFactory 接口的