@Autowired
private DataSourceTransactionManager dataSourceTransactionManager;
List<TransactionStatus> transactionStatuses = Collections.synchronizedList(new ArrayList<>());
@Override
@Transactional(rollbackFor = {Exception.class})
public void testMultiThreadTransactional() throws BizException {
//模拟总数据
List<SysUserAddress> sysUserAddresses = ListUtil.toList();
for (int i = 0; i < 10000; i++) {
sysUserAddresses.add(new SysUserAddress(null, "上海市" + (i + 1), "上海市", "浦东新区"));
}
//线程数,按线程数拆分,默认3个线程
int threadCount = 3;
//按线程数平均分配后,每个线程处理的数据量
int perThreadData = sysUserAddresses.size() / threadCount;
//按线程数平均分配后,多余的数据量
int remainderCount = sysUserAddresses.size() % threadCount;
//有多余的数据,再开个线程处理
boolean havingRemainder = remainderCount > 0;
if (havingRemainder) {
threadCount += 1;
}
//子线程倒计锁
CountDownLatch threadLatchs = new CountDownLatch(threadCount);
//子线程中是否有异常标识
AtomicBoolean isError = new AtomicBoolean(false);
try {
for (int i = 0; i < threadCount; i++) {
//设置每个线程处理的数据量,多余的数据放在最后一个线程中处理
List<SysUserAddress> splitList = sysUserAddresses.stream()
.skip((long) i * perThreadData)
.limit((i == threadCount - 1) ? (havingRemainder ? remainderCount : perThreadData) : perThreadData)
.collect(Collectors.toList());
//开启多线程
executorService.execute(() -> {
try {
try {
this.sysUserAddressService.saveSysUserAddressByTransaMan(dataSourceTransactionManager, transactionStatuses, splitList);
} catch (Throwable e) {
e.printStackTrace();
isError.set(true);
} finally {
threadLatchs.countDown();
}
} catch (Exception e) {
e.printStackTrace();
isError.set(true);
}
});
}
// 倒计锁设置超时时间 30s
boolean await = threadLatchs.await(300, TimeUnit.SECONDS);
// 判断是否超时
if (!await) {
isError.set(true);
log.error("等待子线程执行已经超时!");
}
} catch (Throwable e) {
e.printStackTrace();
isError.set(true);
}
if (CollUtil.isNotEmpty(transactionStatuses)) {
if (isError.get()) {
transactionStatuses.forEach(status -> {
if (!status.isCompleted()) {
dataSourceTransactionManager.rollback(status);
}
});
} else {
transactionStatuses.forEach(status -> {
if (!status.isCompleted()) {
dataSourceTransactionManager.commit(status);
}
});
}
}
System.out.println("主线程完成!");
}
@Override
@Transactional(rollbackFor = {Exception.class})
public void saveSysUserAddressByTransaMan(PlatformTransactionManager transactionManager, List<TransactionStatus> transactionStatuses, List<SysUserAddress> sysUserAddressList) {
if (CollUtil.isEmpty(sysUserAddressList)) {
return;
}
//将事务状态都放在同一个事务里面
DefaultTransactionDefinition def = new DefaultTransactionDefinition();
def.setPropagationBehavior(Propagation.REQUIRES_NEW.value()); // 事物隔离级别,每个线程都开启新事务,会比较安全一些
TransactionStatus transactionStatus = transactionManager.getTransaction(def); // 获得事务状态
transactionStatuses.add(transactionStatus);
sysUserAddressList.forEach(obj -> {
// if (StrUtil.equals(obj.getProvince(), "上海市2")) {
// //模拟子线程中保存出现异常
// int i = 1 / 0;
// }
synchronized (obj) {
save(obj);
}
});
System.out.println("子线程:" + Thread.currentThread().getName());
}
引用:SpringBoot项目中控制线程池、多线程事务提交、回滚的方式_spring多线程事务回滚_拄杖忙学轻声码的博客-CSDN博客