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


    ⚡️ 在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
  • 相关阅读:
    Android平台GB28181设备接入模块CSDN博客汇总
    全内反射棱镜(TIR)的建模
    QFeamework中的MVC
    Android开发学习【Button控件】
    centos7离线时间同步
    MVVM理解+Object.defineproperty方法+数据代理
    基于Java毕业设计学生宿舍管理系统源码+系统+mysql+lw文档+部署软件
    前端教程-H5游戏开发
    nRF52832——大量数据传输时导致蓝牙断开连接,且无法被搜索到的解决方案(广播参数的设置、程序设计方法)
    查找算法 —— 斐波拉契查找法
  • 原文地址:https://blog.csdn.net/Pzzzz_wwy/article/details/126533344