线程出现异常
线程执行任务时,没有添加异常处理。导致任务内部发生异常时,内部错误无法被记录下来。
ini
代码解读
复制代码
Thread thread =new Thread(()->{ if (1 == 1) { log.error("error"); throw new RuntimeException("异常了"); } }); thread.start(); }
可以看到,异常不会打印日志,而是输出在控制台上了。那么异常去了哪里?
异常去了哪里?
正常来说,如果我们进行了异常捕获,是可以看到日志信息的。
php
代码解读
复制代码
Thread thread = new Thread(()->{ try{ log.info("hello"); throw new RuntimeException("运行时异常了"); }catch (Exception e){ log.error("异常发生",e); } }); thread.start();
但是如果是一个未捕获的异常,异常是抛到控制台上的,什么原因呢?
如果一个异常未被捕获,从线程中抛了出来。JVM会回调T hread 类中dispatchUncaughtException
方法。
在这方法中,就是获取一个默认的异常的处理器,我们来看看这个处理器它是怎么做的?
可以看到这里,他不会记录日志信息,而是将异常抛到了控制台中,那么线上我们一般是通过日志来排查问题,所以,我们怎么将控制台的输出转变成日志输出呢?
解决方案
其实,很简单,就是给线程添加一个异常捕获处理器
,以后抛了异常,就给它转成日志。这样才能及时发现问题。
首先定义一个异常处理器:
typescript
代码解读
复制代码
/** * 自定义异常处理器(将异常时控制台输出,装变成日志输出) */ @Slf4j public class MyUncaughtExceptionHandler implements Thread.UncaughtExceptionHandler { @Override public void uncaughtException(Thread t, Throwable e) { log.error("Exception Thread",e); } }
设置异常处理器
arduino
代码解读
复制代码
Thread thread =new Thread(()->{ if (1 == 1) { log.error("error"); throw new RuntimeException("异常了"); } }); // 设置异常处理器 thread.setUncaughtExceptionHandler(new MyUncaughtExceptionHandler()); thread.start();
可以看到即使没有获取异常,抛出的异常,我们也记录在日志中。
线程池如何进行异常处理呢?
可以通过线程池的ThreadFactory
,创建线程的工厂,创建线程的时候给线程添加异常捕获。
首先自定义线程工厂,然后就是在保持原有线程工厂的能力,然后额外的配置我们自己自定义的异常处理器。
arduino
代码解读
复制代码
/** * 自定义线程工厂(提供处理异常的能力) */ 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提供的线程池。
scss
代码解读
复制代码
@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
接口的