介绍
项目中,我们通常需要统一管理线程池,统一管理线程池,有以下几个优点:
- 资源控制:创建过多的线程池,会导致系统资源的过多消耗。
- 提升性能:创建可以重复使用的线程,可以提高系统性能,因为省去了创建线程、销毁线程的时间。
- 自定义功能:通过统一管理线程池,可以自定义系统功能,提升系统性能。
- 提升问题定位:通过统一管理线程池,可以控制异常打印,快速定位问题
如何捕获异常
Thread有两个属性,一个实例变量,一个类静态变量
区别是:实例变量,对当前实例生效;类静态变量对所有线程生效。
一般来说,我们只设置实例变量,我们只希望也只有精力做自己的事没有精力去管其他人的事。
所以,我们自定义异常处理器时,只需要设置实例变量即可。
那么如何捕获线程池中线程的异常呢,由于我们使用的是spring封装的线程池。创建线程的行为已经被封装了,很难自定义,但是通过ThreadPoolTaskExecutor的UML类图,可以看出ThreadPoolTaskExecutor继承了ExecutorConfigurationSupport,而ExecutorConfigurationSupport中将本身设置为了ThreadFactory(因为ExecutorConfigurationSupport实现了ThreadFactory接口,所以可以这样设置),并且ExecutorConfigurationSupport中还提供了一个设置ThreadFactory的方法,可以设置自定义的ThreadFactory,这提供了一个解耦的机会。我们可以自定义ThreadFactory来在创建线程时给线程设置异常处理器。
代码
引入配置:
@Configuration
@EnableAsync
public class ThreadPoolConfig implements AsyncConfigurer {
/**
* 项目共用线程池
*/
public static final String BADBOYCHAT_EXECUTOR = "badboychatExecutor";
/**
* 重写方法以指定当使用注解@Async时使用我们自定义的Executor
*/
@Override
public Executor getAsyncExecutor() {
return badboychatExecutor();
}
@Bean(BADBOYCHAT_EXECUTOR)
@Primary
public ThreadPoolTaskExecutor badboychatExecutor() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.setCorePoolSize(10);
executor.setMaxPoolSize(10);
executor.setQueueCapacity(200);
// 设置优雅停机
executor.setWaitForTasksToCompleteOnShutdown(true);
executor.setThreadNamePrefix("badboychat-executor-");
// 自定义的ThreadFactory,设置了UncaughtExceptionHandler,将异常日志打印出来,方便排查错误
executor.setThreadFactory(new MyThreadFactory(executor));
executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());//满了调用线程执行,认为重要任务
executor.initialize();
return executor;
}
}
MyThreadFactory为自定义的ThreadFactory,重写newThread方法,设置UncaughtExceptionHandler为自定义的异常处理器:
@Slf4j
@AllArgsConstructor
public class MyThreadFactory implements ThreadFactory {
private static final MyUncaughtExceptionHandler MY_UNCAUGHT_EXCEPTION_HANDLER = new MyUncaughtExceptionHandler();
private ThreadFactory factory;
@Override
public Thread newThread(Runnable r) {
Thread thread = factory.newThread(r);
thread.setUncaughtExceptionHandler(MY_UNCAUGHT_EXCEPTION_HANDLER);
return thread;
}
}
MyUncaughtExceptionHandler为自定义的异常处理器,这个异常处理器是所有实例公用的,所以可以写成单例模式,即
private static final MyUncaughtExceptionHandler MY_UNCAUGHT_EXCEPTION_HANDLER = new MyUncaughtExceptionHandler();
MyUncaughtExceptionHandler代码如下:
@Slf4j
public class MyUncaughtExceptionHandler implements Thread.UncaughtExceptionHandler {
@Override
public void uncaughtException(Thread t, Throwable e) {
log.error("线程{}发生异常,异常信息为:{}",t.getName(),e.getMessage());
}
}
这里自定义的异常处理器需要实现Thread的实例变UncaughtExceptionHandler ,原因上面已经说明过。
至此,统一管理线程池时,捕获线程的异常就已经实现了。