关于多线程操纵数据库手动提交事务保证事务一致性的坑

关于多线程操纵数据库手动提交事务保证事务一致性的坑

思路:
主线程开启多个子线程,每个子线程开启自己的事务不提交,等所有数据都在子线程的事务里,且没有子线程报错的情况下,所有子线程再一起提交,发现任意一个子线程报错,所有子线程回滚。
问题:
1.假设我有8个线程,10条数据,每条数据开启一个子线程,当事务开启到第7个的时候,第8个就创建不出来了,因为线程这时候都被占用了(7子+1主),程序陷入死循环,一直等待。
2.假设我8个线程,5条数据,问题1不会出现,这时候saveOrUpdateDataBase()方法如果我用mybatis-plus的saveBatch方法去实现,会报错,初步分析,是第一个执行完commit的子线程会关闭sqlsession,导致第二个将要执行完的子线程commit的时候找不到sqlsession了,报错。

@Service
public interface SyncSaveOrUpdateTransactionService<T> {
    Logger log = LoggerFactory.getLogger(SyncSaveOrUpdateTransactionService.class);
    default void syncSaveOrUpdate(List<T> list) throws ExecutionException, InterruptedException {
        List<List<T>> partition = Lists.partition(list, 1);
        List<TransactionStatus> statusList = Collections.synchronizedList(Lists.newArrayList());
        List<CompletableFuture<Boolean>> futureList = Collections.synchronizedList(Lists.newArrayList());
        DataSourceTransactionManager transactionManager = getDataSourceTransactionManager();
        ThreadPoolTaskExecutor threadPoolTaskExecutor = getThreadPoolTaskExecutor();
        for (List<T> subList : partition) {

            CompletableFuture<Boolean> booleanFuture = CompletableFuture.supplyAsync(() -> {
                try {
                    DefaultTransactionDefinition definition = new DefaultTransactionDefinition();
                    definition.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRES_NEW);
                    TransactionStatus status = transactionManager.getTransaction(definition);
                    statusList.add(status);
                    log.info("开启事务{}",statusList.size());
                    saveOrUpdateDataBase(subList);
                } catch (TransactionException e) {
                    return false;
                }
                return true;
            }, threadPoolTaskExecutor).exceptionally(e -> false);
            futureList.add(booleanFuture);

        }
        if (!CollectionUtils.isEmpty(futureList)) {
            boolean isSuccessful = Boolean.TRUE;
            for (CompletableFuture<Boolean> future : futureList) {
                if (!future.get()) {
                    // 所有子线程回滚
                    isSuccessful = Boolean.FALSE;
                }
            }
            if (isSuccessful) {
                // 所有子线程提交
                log.info("所有子线程提交{}",statusList.size());
                statusList.forEach(transactionManager::commit);
            }else {
                log.info("所有子线程回滚{}",statusList.size());
                statusList.forEach(transactionManager::rollback);
            }
        }
    }

    void saveOrUpdateDataBase(List<T> subList);

    DataSourceTransactionManager getDataSourceTransactionManager();

    ThreadPoolTaskExecutor getThreadPoolTaskExecutor();
}
  • 2
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
在Spring Boot中,如果你在多线程环境下需要手动提交事务,通常不能直接使用声明式事务@Transactional()来控制事务,因为每个线程都是一个单独的事务。为了解决这个问题,你可以使用TransactionTemplate和PlatformTransactionManager来手动执行和提交事务。 首先,你需要在你的类中注入PlatformTransactionManager来获取当前的事务。然后,你可以使用TransactionTemplate类来执行事务。比如,你可以使用TransactionTemplate的execute方法,在其中执行你的业务逻辑。如果你需要在事务中执行多个操作,可以在这个方法中编写代码。最后,你可以使用PlatformTransactionManager的commit方法手动提交事务。以下是一个示例代码: ``` @Autowired private TransactionTemplate transactionTemplate; @Autowired private PlatformTransactionManager transactionManager; public void updateData() { transactionTemplate.execute(new TransactionCallbackWithoutResult() { @Override protected void doInTransactionWithoutResult(TransactionStatus status) { // 执行你的业务逻辑 // 如果你想在事务中执行多个操作,可以在此处编写代码 // 手动提交事务 transactionManager.commit(status); } }); } ``` 在这个示例中,我们使用TransactionTemplate的execute方法来执行事务,然后使用PlatformTransactionManager的commit方法来手动提交事务。这样,你就可以在多线程环境下手动提交事务了。<span class="em">1</span><span class="em">2</span><span class="em">3</span><span class="em">4</span>

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值