如何在多线程异步的情况下保证事务?

11 篇文章 0 订阅
10 篇文章 38 订阅

⚡️ 在Spring环境下,如果使用了 @Transactional 注解,那么当你的 inert 操作时异步的话,则会不在当前事务里面,那么后续的回滚操作,不会将这次异步操作的插入进行回滚,那么我们有方式来保证多线程异步场景下的事务吗?

@Service
public class TransactionAsyncService {
    @Autowired
    private PersonService personService;

    @Transactional
    void transactionAsync() {
        new Thread(() -> {
            personService.insertPerson("Cocowwy-1");
        }).start();

        personService.insertPerson("Cocowwy-2");

        throw new RuntimeException("手动回滚");
    }
}

😶 在上面的Demo中,是开了一个新的线程对数据库插入了数据 Cocowwy-1 这一条数据,之后再在事务里面插入了数据 Cocowwy-2 之后再手动抛了一个异常执行了回滚的操作。
有经验的开发🐒应该知道,这几行代买应该只会回滚 Cocowwy-2 这行代码,因为异步了,由于ThreadLocal的特性,导致事务是不会传递到异步线程里面的。

那么有什么办法可以做到保证子线程的一致性吗??

⚡️灵机一动⚡️,可以利用 JUC的循环栅栏CyclicBarrier,来手动控制多线程事务的统一提交,手动事务提交可以参考这篇文章:戳我
大致思路是这样,对多线程均开启一个事务,并用循环栅栏挡住线程,当确定所有线程均能够正常插入数据之后,再手动提交事务,如果存在插入失败的线程,则全部线程进行手动回滚。
实现代码大致如下:

/**
 * 实现在事务内进行多线程inert操作,并保证事务
 * @author cocowwy.cn
 * @create 2022-05-05-11:45
 */
@Service
public class TransactionAsyncService {
    @Autowired
    private DataSourceTransactionManager dataSourceTransactionManager;
    @Autowired
    private TransactionDefinition transactionDefinition;
    @Autowired
    private PersonService personService;

    @Transactional
    void transactionAsync() {
        CyclicBarrier cb = new CyclicBarrier(10);

        AtomicReference<Boolean> rollback = new AtomicReference<>(false);

        for (int i = 0; i < 10; i++) {
            int currentNum = i;

            new Thread(() -> {
                // 手动开启事务
                TransactionStatus transaction = dataSourceTransactionManager.getTransaction(transactionDefinition);
                try {
                    // insert操作,如果插入数据<1则抛异常
                    if (personService.insertPerson("Cocowwy-" + currentNum) < 1) {
                        throw new RuntimeException("插入数据失败");
                    }
                    // 等待所有线程的事务结果
                    cb.await();
                    // 如果标志需要回滚,则回滚
                    if (rollback.get()) {
                        dataSourceTransactionManager.rollback(transaction);
                        return;
                    }

                    dataSourceTransactionManager.commit(transaction);
                } catch (Exception e) {
                    // 如果当前线程执行异常,则设置回滚标志
                    rollback.set(true);
                    dataSourceTransactionManager.rollback(transaction);
                    throw new RuntimeException(e);
                }
            }).start();
        }
    }
}
  • 4
    点赞
  • 33
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
多线程异步操作回滚事务可以通过使用`@Transactional`注解和`TransactionSynchronizationManager`类来实现。具体步骤如下: 1.在异步方法上添加`@Transactional(rollbackFor = Exception.class)`注解,表示该方法需要进行事务管理,并且在出现异常时需要回滚事务。 2.在同步方法中调用异步方法时,使用`TransactionSynchronizationManager.registerSynchronization()`方法注册一个事务同步器,该同步器会在事务提交或回滚时被调用。 3.在事务同步器中调用`TransactionSynchronizationManager.isActualTransactionActive()`方法判断当前是否有活动的事务,如果有,则调用`TransactionAspectSupport.currentTransactionStatus().setRollbackOnly()`方法将事务标记为回滚状态。 下面是一个示例代码: ```java @Service public class MyService { @Autowired private MyAsyncService myAsyncService; @Transactional(rollbackFor = Exception.class) public void doSomething() { // 业务代码... try { // 数据库操作... } catch (Exception e) { // 调用异步方法完成数据库操作 myAsyncService.doSomethingAsync(); // 注册一个事务同步器 TransactionSynchronizationManager.registerSynchronization(new TransactionSynchronizationAdapter() { @Override public void afterCompletion(int status) { // 判断当前是否有活动的事务 if (TransactionSynchronizationManager.isActualTransactionActive()) { // 将事务标记为回滚状态 TransactionAspectSupport.currentTransactionStatus().setRollbackOnly(); } } }); } } } @Service public class MyAsyncService { @Async @Transactional(rollbackFor = Exception.class) public void doSomethingAsync() { // 异步方法业务代码... try { // 异步方法数据库操作... } catch (Exception e) { // 抛出异常,事务会自动回滚 throw new RuntimeException(e); } } } ```
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值