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


    ⚡️ 在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("手动回滚");
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    😶 在上面的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 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();
            }
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
  • 相关阅读:
    轻松连接:简化编程中的连接操作
    windows频繁更新问题解决方案
    Vue组件
    css样式重置
    【计时器】
    【软件测试】资深测试聊,自动化测试分层实践,彻底打通高阶...
    java多线程
    【lombok】equals相等返回false contains包含返回false? lombok注解的一个天坑
    支付宝小程序 alipays协议链接跳转
    服务器感染了.locked勒索病毒,如何确保数据文件完整恢复?
  • 原文地址:https://blog.csdn.net/Pzzzz_wwy/article/details/126533344