当前位置: 首页 > news >正文

SpringBoot多线程,保证各个子线程和主线程事物一致性

SpringBoot多线程,保证各个子线程和主线程事物一致性

  • 1、第一种写法
    • 1.1、TransactionalUntil工具类
    • 1.2、service业务类
  • 2、第二种写法
    • 2.1、service业务类

1、第一种写法

1.1、TransactionalUntil工具类

import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import org.springframework.stereotype.Component;
import org.springframework.transaction.TransactionStatus;
import org.springframework.transaction.interceptor.DefaultTransactionAttribute;

import javax.annotation.Resource;

@Component
public class TransactionalUntil {

    @Resource
    private DataSourceTransactionManager dataSourceTransactionManager;

    /**
     * 开启事务
     */
    public TransactionStatus begin() {
        TransactionStatus transaction = dataSourceTransactionManager.getTransaction(new DefaultTransactionAttribute());
        return transaction;
    }

    /**
     * 提交事务
     */
    public void commit(TransactionStatus transactionStatus) {
        dataSourceTransactionManager.commit(transactionStatus);
    }

    /**
     * 回滚事务
     */
    public void rollback(TransactionStatus transactionStatus) {
        dataSourceTransactionManager.rollback(transactionStatus);
    }

}

1.2、service业务类

其实主要就是用CountDownLatch类的特性,和手动提交事物,来保证主线程和子线程事物的一致性。

    @Resource
    private TransactionalUntil transactionalUntil;
    
    private volatile Boolean is_ok = new Boolean(true);

    @Transactional
    @Override
    public void trs1() throws Exception{
        // 监控子线程数据
        CountDownLatch childMonitor = new CountDownLatch(2);
        // 主线程收集子线程运行最终结果
        List<Boolean> childResponse = new CopyOnWriteArrayList<>();
        //子线程在该对象上等待主线程的通知
        CountDownLatch mainMonitor = new CountDownLatch(1);
        
		//业务代码查询,可以换成自己的业务场景
        ResVmCustom resVmCustom1 = resVmCustomMapper.selectById(1);
        resVmCustom1.setCpu(3);
        resVmCustomMapper.updateById(resVmCustom1);


        CompletableFuture.runAsync(() -> {
            TransactionStatus transactionStatus = transactionalUntil.begin();
            try {
                ResVmCustom resVmCustom2 = resVmCustomMapper.selectById(2);
                resVmCustom2.setCpu(3);
                //Db.saveOrUpdate(resVmCustom2);
                resVmCustomMapper.updateById(resVmCustom2);

                childResponse.add(Boolean.TRUE);
                childMonitor.countDown();
                mainMonitor.await();
                if (is_ok) {
                    // 事务提交
                    log.info("线程  {} 正常执行,线程事务提交", Thread.currentThread().getName());
                    transactionalUntil.commit(transactionStatus);
                } else {
                    // 事务回滚
                    log.info("线程   {} 执行出现异常,线程事务回滚", Thread.currentThread().getName());
                    transactionalUntil.rollback(transactionStatus);
                }
            } catch (Exception e) {
                // 提交失败
                log.info("线程   {} 执行出现异常", Thread.currentThread().getName());
                childMonitor.countDown();
                childResponse.add(Boolean.FALSE);
                transactionalUntil.rollback(transactionStatus);
            }
        });


        CompletableFuture.runAsync(() -> {
            TransactionStatus transactionStatus = transactionalUntil.begin();
            try {
                ResVmCustom resVmCustom3 = resVmCustomMapper.selectById(3);
                resVmCustom3.setCpu(3);
                resVmCustomMapper.updateById(resVmCustom3);

                childResponse.add(Boolean.TRUE);
                childMonitor.countDown();
                mainMonitor.await();
                if (is_ok) {
                    // 事务提交
                    log.info("线程  {} 正常执行,线程事务提交", Thread.currentThread().getName());
                    transactionalUntil.commit(transactionStatus);
                } else {
                    // 事务回滚
                    log.info("线程   {} 执行出现异常,线程事务回滚", Thread.currentThread().getName());
                    transactionalUntil.rollback(transactionStatus);
                }
            } catch (Exception e) {
                // 提交失败
                log.info("线程   {} 执行出现异常", Thread.currentThread().getName());
                childMonitor.countDown();
                childResponse.add(Boolean.FALSE);
                transactionalUntil.rollback(transactionStatus);
            }
        });


        childMonitor.await();
        for (Boolean res : childResponse) {
            while (!res) {
                // 如果有一个子线程只想失败,改变mainResult状态 事务回滚
                log.info("有线程执行失败,修改标识位,事务回滚");
                is_ok = false;
                break;
            }
        }
        //主线程获取结果,子线程根据主线程的结果 提交或回滚
        mainMonitor.countDown();
        /**
         * 下面这句话,保证了主线程和子线程一致
         * 如果不想主线程和子线程事物保持一致,注释下面这句话就行
         */
        if (!is_ok) {
            throw new RuntimeException();
        }
    }

2、第二种写法

2.1、service业务类

TransactionalUntil 工具类还用和上面的一样就行…

    /**
     * SpringBoot默认的自己的线程池
     */
    @Resource
    private ThreadPoolTaskExecutor executor;

    @Transactional
    @Override
    public void trs2() throws Exception {
        // 假设三个线程要执行
        // 监控子线程数据
        CountDownLatch childMonitor = new CountDownLatch(3);
        // 主线程收集子线程运行最终结果
        List<Boolean> childResponse = new CopyOnWriteArrayList<>();
        //子线程在该对象上等待主线程的通知
        CountDownLatch mainMonitor = new CountDownLatch(1);
        for (int i = 1; i <= 3; i++) {
            int finalI = i;
            executor.execute(() -> {
                TransactionStatus transactionStatus = transactionalUntil.begin();
                try {
                    //数据库增删改操作
                    //此时只是用线程的个数i,当成业务的主键id,你自己重新声明其他变量来替代主键业务ID也行
                    ResVmCustom resVmCustom = resVmCustomMapper.selectById(finalI);
                    resVmCustom.setCpu(3);
                    resVmCustomMapper.updateById(resVmCustom);
                    childResponse.add(Boolean.TRUE);
                    childMonitor.countDown();
                    mainMonitor.await();
                    if (is_ok) {
                        // 事务提交
                        log.info("线程  {} 正常执行,线程事务提交", Thread.currentThread().getName());
                        transactionalUntil.commit(transactionStatus);
                    } else {
                        // 事务回滚
                        log.info("线程   {} 执行出现异常,线程事务回滚", Thread.currentThread().getName());
                        transactionalUntil.rollback(transactionStatus);
                    }
                } catch (Exception e) {
                    // 提交失败
                    log.info("线程   {} 执行出现异常", Thread.currentThread().getName());
                    childMonitor.countDown();
                    childResponse.add(Boolean.FALSE);
                    transactionalUntil.rollback(transactionStatus);
                }
            });
        }

        childMonitor.await();
        for (Boolean res : childResponse) {
            while (!res) {
                // 如果有一个子线程只想失败,改变mainResult状态 事务回滚
                log.info("有线程执行失败,修改标识位,事务回滚");
                is_ok = false;
                break;
            }
        }
        is_ok = false;
        //主线程获取结果,子线程根据主线程的结果 提交或回滚
        mainMonitor.countDown();
        /**
         * 下面这句话,保证了主线程和子线程一致
         * 如果不想主线程和子线程事物保持一致,注释下面这句话就行
         */
        if (!is_ok) {
            throw new RuntimeException();
        }
    }
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.dtcms.com/a/124990.html

相关文章:

  • 使用 Rsync + Lsyncd 实现 CentOS 7 实时文件同步
  • 双相机结合halcon的条码检测
  • 大模型论文:CRAMMING TRAINING A LANGUAGE MODEL ON ASINGLE GPU IN ONE DAY(效率提升)-final
  • LeetCode 解题思路 36(Hot 100)
  • 自适应LL解析的终极进化:ALL(*)算法如何改写语法解析规则
  • 动态词槽管理系统深度设计
  • YOLO11改进-模块-引入门控瓶颈卷积GBC 关注目标抑制背景干扰
  • OpenEuler运维实战-(OS|硬件信息-软件信息-日志)信息收集!
  • Linux服务器网卡深度解析:从ifconfig输出到生产环境性能调优实战
  • 力扣刷题Day 15:二叉树中的最大路径和(124)
  • [ctfshow web入门] web32
  • 【场景应用1】微调语言模型:从数据加载到模型训练、模型评估
  • VMware Workstation/Player 在 Windows 上的完整安装与使用指南
  • [bug]解决vscode+cline使用mcp服务报错spawn npx enoent spawn npx enoent
  • 7. RabbitMQ 消息队列——延时队列(Spring Boot + 安装message_exchange“延迟插件“ 的详细配置说明)的详细讲解
  • 进程通信的学习
  • 多值字典表设计:优雅处理一对多关系的数据库方案
  • C++基础精讲-01
  • Ubuntu环境下,EDK2+EmulatorPkg编译运行UEFI固件
  • Ollama教程与大模型本地部署指南
  • 32 python json
  • PostgreSQLs数据库考试
  • MySQL 中的聚簇索引和非聚簇索引有什么区别?
  • Redis的过期和内存淘汰策略
  • 新技术学习方法
  • 卷积神经网络 CNN 系列总结(一)---基础知识点
  • ubuntu22.04 安装-ODBC驱动-SQLserver
  • vue中根据html动态渲染内容2.0
  • 无人设备遥控器之数据分析与处理篇
  • 一周学会Pandas2 Python数据处理与分析-Pandas2数据读取