问题记录:使用多线程来处理任务子线程出现异常不回滚
解决思路:在子线程任务中手动提交回滚事务
顺便记录一下线程池的重要执行节点:
- 当任务来了之后,判断线程池中实际线程数是否小于核心线程数,如果小于就直接创建并执行任务。
- 当实际线程数大于核心线程数,他就会判断任务队列是否已满,如果未满就将任务存放队列即可。
- 判断线程池的实际线程数是否大于最大线程数,如果小于最大线程数,直接创建线程执行任务;实际线程数已经等于最大线程数,则会直接执行拒绝策略。
/**
*
* @param corePoolSize 核心线程数
* @param maximumPoolSize 最大线程数
* @param keepAliveTime 多余线程空余等待时间
* @param unit 单位
* @param workQueue 任务队列(需要手动指定最大任务数量,默认为Integer.MAX_VALUE,可能会内存溢出)
* @param handler 任务拒绝策略
* CallerRunsPolicy使用调用者的线程来执行任务
* AbortPolicy拒绝执行,提示异常(默认)
* DiscardPolicy拒绝该任务
* DiscardOldestPolicy忽略最旧的任务
*/
private static final ThreadPoolExecutor poolExecutor = new ThreadPoolExecutor(5, 5, 1, TimeUnit.MINUTES, new LinkedBlockingQueue<>(50), new ThreadPoolExecutor.CallerRunsPolicy());
@Override
@Transactional
public ApiResult testThread() throws ExecutionException, InterruptedException {
ApproveLog approveLog = new ApproveLog();
approveLog.setAuditorRemark("主线程插入数据测试!");
approveLogMapper.insert(approveLog);
CompletableFuture<String> future = CompletableFuture.supplyAsync(() -> {
/*
手动开启事务
*/
DefaultTransactionDefinition def = new DefaultTransactionDefinition();
// 事物隔离级别,开启新事务,这样会比较安全些。
def.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRES_NEW);
// 获得事务状态
TransactionStatus status = dataSourceTransactionManager.getTransaction(def);
try {
approveLog.setAuditorRemark("子线程插入数据测试!");
approveLogMapper.insert(approveLog);
// 异常
int a = 10 / 0;
dataSourceTransactionManager.commit(status);
} catch (Exception e) {
log.error("有异常了!!!!!!!!");
// 事务回滚
dataSourceTransactionManager.rollback(status);
throw new RuntimeException(e);
}
return "成功";
}, poolExecutor).exceptionally(e -> {
log.error("返回异常了!!!!!,{}", e);
return "500";
});
// 如果返回500则是出现异常,手动抛异常回滚主线程
if (future.get().equals("500")) {
throw new RuntimeException("500");
}
return ApiResult.ok();
}