线程池中的线程抛异常问题
线程抛异常问题
execute方法中的run方法抛出异常
@Slf4j
@Service
public class MenuServiceImpl {
@Autowired
private MenuMapper menuMapper;
@Autowired
private ThreadPoolExecutor threadPoolExecutor;
public List<MenuDTO> listMenu() throws Exception {
threadPoolExecutor.execute(new Runnable() {
@Override
public void run() {
log.info("==================== 进入execute的run方法");
int i = 1 / 0;
}
});
List<MenuEntity> menuEntityList = menuMapper.selectMenuList();
log.info("==================== menuEntityList : {}", menuEntityList);
return CopyUtils.copyListByShallow(menuEntityList, MenuDTO.class);
}
}
结果:
说明:
可以看到execute方法执行的线程,抛出了计算异常,控制台打印了异常信息,但是主线程仍然可以继续执行。
submit方法中的run方法抛出异常
@Slf4j
@Service
public class MenuServiceImpl {
@Autowired
private MenuMapper menuMapper;
@Autowired
private ThreadPoolExecutor threadPoolExecutor;
public List<MenuDTO> listMenu() throws Exception {
Future<Integer> future = threadPoolExecutor.submit(new Callable<Integer>() {
@Override
public Integer call() throws Exception {
log.info("==================== 进入submit的run方法");
int i = 1 / 0;
return i;
}
});
List<MenuEntity> menuEntityList = menuMapper.selectMenuList();
log.info("==================== menuEntityList : {}", menuEntityList);
future.get();
return CopyUtils.copyListByShallow(menuEntityList, MenuDTO.class);
}
}
结果:
说明:
当调用future.get();方法的时候,将会抛出异常,而如果未调用get方法,那么将不会抛出异常。
异常处理
方法1,使用try-catch处理异常
threadPoolExecutor.execute(new Runnable() {
@Override
public void run() {
try {
log.info("==================== 进入execute的run方法");
int i = 1 / 0;
} catch (Exception e) {
log.info("==================== 1 / 0 异常 : " + e);
}
}
});
方法2,使用Thread.setDefaultUncaughtExceptionHandler方法处理异常
方法1可以捕获异常,但是每段代码都写try-catch实在是太麻烦了,在创建线程时可以用Thread.setDefaultUncaughtExceptionHandler方法捕获异常。
@Bean
@Scope("singleton")
public ThreadPoolExecutor getExecutor() {
ThreadPoolExecutor executor = new ThreadPoolExecutor(corePoolSize, maxPoolSize,
keepAliveSeconds, TimeUnit.SECONDS,
new LinkedBlockingQueue<>(queueCapacity), new InnerThreadFactory(),
new ThreadPoolExecutor.CallerRunsPolicy() {
@Override
public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
if (!e.isShutdown()) {
r.run();
}
}
});
executor.allowCoreThreadTimeOut(allowCoreThreadTimeOut);
return executor;
}
static class InnerThreadFactory implements ThreadFactory {
@Override
public Thread newThread(Runnable r) {
Thread thread = new Thread(r);
LocalDateTime ldt = LocalDateTime.now();
DateTimeFormatter pattern = DateTimeFormatter.ofPattern("yyyyMMd HH:mm:ss");
// 设置线程名称
thread.setName(THREAD_PREFIX + ldt.format(pattern));
// 设置线程的异常处理
thread.setUncaughtExceptionHandler(new Thread.UncaughtExceptionHandler() {
@Override
public void uncaughtException(Thread t, Throwable e) {
String msg = "线程ID : " + t.getId() +
" 线程名 : " + t.getName() +
" 异常 : " + e;
log.info(msg);
}
});
return thread;
}
}
- 当使用submit提交的时候,使用Thread.setDefaultUncaughtExceptionHandler方法却无法捕获异常
Future<Integer> future = threadPoolExecutor.submit(new Callable<Integer>() {
@Override
public Integer call() throws Exception {
log.info("==================== 进入submit的run方法");
int i = 1 / 0;
return i;
}
});
future.get();
原因 - submit源码:
public <T> Future<T> submit(Callable<T> task) {
if (task == null) throw new NullPointerException();
RunnableFuture<T> ftask = newTaskFor(task);
execute(ftask);
return ftask;
}
protected <T> RunnableFuture<T> newTaskFor(Callable<T> callable) {
return new FutureTask<T>(callable);
}
get方法:
又把outcome存放异常对象搬出来了:
方法3,afterExecute方法,钩子方法,重写afterExecute方法
注意,由于submit方法会吞噬异常,所以重写afterExecute方法生效,只会对execute方法有效。
ThreadPoolExecutor executor = new ThreadPoolExecutor(corePoolSize, maxPoolSize,
keepAliveSeconds, TimeUnit.SECONDS,
new LinkedBlockingQueue<>(queueCapacity), new InnerThreadFactory(),
new ThreadPoolExecutor.CallerRunsPolicy() {
@Override
public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
if (!e.isShutdown()) {
r.run();
}
}
}) {
@Override
protected void afterExecute(Runnable r, Throwable t) {
String msg = "线程池报异常 : " + t;
log.info(msg);
}
};