Springboot 多线程实现事务控制

2023-11-03

业务背景

工作中有很多,在一个service实现层,有可能调用其他的service,或者说是使用异步线程,这样就不容易控制当前操作成功和失败
不多说,上代码

获取数据库事务控制器

@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);
    }

}

测试代码

package com.demo.easypdf;

import cn.hutool.core.thread.ThreadUtil;
import com.beust.jcommander.internal.Lists;
import com.demo.easypdf.domain.TestOne;
import com.demo.easypdf.domain.TestT;
import com.demo.easypdf.domain.User;
import com.demo.easypdf.mapper.TestOneMapper;
import com.demo.easypdf.mapper.TestTMapper;
import com.demo.easypdf.mapper.UserMapper;
import com.demo.easypdf.untils.TransactionalUntil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.transaction.TransactionStatus;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import wiki.xsx.core.pdf.component.text.XEasyPdfText;
import wiki.xsx.core.pdf.doc.XEasyPdfDocument;
import wiki.xsx.core.pdf.doc.XEasyPdfPage;
import wiki.xsx.core.pdf.handler.XEasyPdfHandler;
import wiki.xsx.core.pdf.template.doc.XEasyPdfTemplateDocument;
import wiki.xsx.core.pdf.template.doc.component.block.XEasyPdfTemplateBlockContainer;
import wiki.xsx.core.pdf.template.doc.component.image.XEasyPdfTemplateImage;
import wiki.xsx.core.pdf.template.doc.component.page.XEasyPdfTemplateCurrentPageNumber;
import wiki.xsx.core.pdf.template.doc.component.page.XEasyPdfTemplateTotalPageNumber;
import wiki.xsx.core.pdf.template.doc.component.text.XEasyPdfTemplateText;
import wiki.xsx.core.pdf.template.doc.page.XEasyPdfTemplatePage;
import wiki.xsx.core.pdf.template.handler.XEasyPdfTemplateHandler;

import javax.annotation.Resource;
import java.util.Collections;
import java.util.List;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.atomic.AtomicBoolean;

/**
 * @Author: lkz
 * @Title: TestController
 * @Description: TODO
 * @Date: 2023/4/27 23:17
 */
@RestController
@Slf4j
public class TestController {

    @Resource
    private UserMapper userMapper;
    @Resource
    private TestOneMapper testOneMapper;
    @Resource
    private TestTMapper testTMapper;

    private volatile  Boolean is_ok=new Boolean(true);

    @Resource
    private TransactionalUntil transactionalUntil;

    @GetMapping("test2")
    public void testThreadTran(){
        // 监控子线程数据
        CountDownLatch childMonitor = new CountDownLatch(2);
        // 主线程收集子线程运行最终结果
        List<Boolean> childResponse= new CopyOnWriteArrayList<>();
        //子线程在该对象上等待主线程的通知
        CountDownLatch mainMonitor = new CountDownLatch(1);

        ThreadUtil.execAsync(()->{

            // 开启事务
            TransactionStatus transactionStatus = transactionalUntil.begin();
            try {
                // 操作数据库逻辑
                List<User> user = this.getUserList();
                userMapper.insert(user.get(0));
                childResponse.add(Boolean.TRUE);
                childMonitor.countDown();

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

        });

        ThreadUtil.execAsync(()->{
            // 开启事务
            TransactionStatus transactionStatus = transactionalUntil.begin();
            try {
                // 操作数据库逻辑
                List<TestT> testTList = this.getTestTList();
                testTMapper.insert(testTList.get(0));
                log.info("test-线程{}正常执行结束,等待其他线程执行结束",Thread.currentThread().getName());
                // 模拟异常
              //  int a=10/0;
                childResponse.add(Boolean.TRUE);
                childMonitor.countDown();
                mainMonitor.await();
                if(is_ok){
                    // 事务提交
                    log.info("test-线程正常执行,线程事务提交",Thread.currentThread().getName());
                    transactionalUntil.commit(transactionStatus);
                }else{
                    // 事务回滚
                    log.info("test-线程执行出现异常,线程事务回滚",Thread.currentThread().getName());
                    transactionalUntil.rollback(transactionStatus);
                }
            }catch (Exception e){
                // 提交失败
                log.info("test-线程执行出现异常{},",Thread.currentThread().getName());
                childMonitor.countDown();
                childResponse.add(Boolean.FALSE);
                transactionalUntil.rollback(transactionStatus);
            }
        });

        try {
            childMonitor.await();
            for(Boolean res:childResponse){
                while (!res){
                    // 如果有一个子线程只想失败,改变mainResult状态 事务回滚
                    log.info("有线程执行失败,修改标识位,事务回滚");
                    is_ok=false;
                    break;
                }
            }
            //主线程获取结果,子线程根据主线程的结果 提交或回滚
            mainMonitor.countDown();
            System.out.println("执行结束-------");
        }catch (Exception e){
            log.info("事务执行失败");
        }

    }


    public List<User> getUserList(){
        List<User> users= Lists.newArrayList();
        for(int i=0;i<20;i++){
            User user = new User();
            user.setId(i);
            user.setUsername("小"+i);
            user.setPassword("asc");
            user.setAge(i+0);
            users.add(user);
        }
        return users;
    }

    public List<TestT> getTestTList(){
        List<TestT> TestT= Lists.newArrayList();
        for(int i=0;i<20;i++){
            TestT tt = new TestT();
            tt.setPhone("i"+"-1234537");
            tt.setId(i);
            TestT.add(tt);
        }
        return TestT;
    }
    public List<TestOne> getTestOneList(){
        List<TestOne> oneList= Lists.newArrayList();
        for(int i=0;i<20;i++){
            TestOne tt = new TestOne();
            tt.setName("i"+"-1234537");
            oneList.add(tt);
        }
        return oneList;
    }

}

测试结果

没有异常

表1
在这里插入图片描述
表二
在这里插入图片描述
控制台打印
在这里插入图片描述

模拟异常

控制台
在这里插入图片描述
数据库无新增
在这里插入图片描述

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Springboot 多线程实现事务控制 的相关文章

随机推荐

  • Vue3 PC端自适应

    安装依赖包 npm i lib flexible save npm install px2rem loader 引入 在main js中引入 import lib flexible flexible 修改flexible js文件 在项目下
  • C#学习之Dispose

    什么是资源 首先要提出 什么是资源 在CLR出来之后 Windows系统资源开始分为 非托管资源 和 托管资源 非托管资源是指 所有的Window内核对象 句柄 都是非托管资源 如对于Stream 数据库连接 GDI 的相关对象 还有Com
  • Ubuntu下安装goLand

    Ubuntu下安装GoLand 官网下载golang包 Other Versions GoLand 下载的是goland 2018 1 6 tar版本 更高的版本不容易激活 将程序解压至 usr local tar zxvf goland
  • 知识体系之APUE/内核编程

    目录 一 APUE 内核编程 1 基本概念与实现 1 1 进程3态 1 1 1 进程调度的方式 1 1 2 调度原则 1 1 3 调度算法 1 2 僵尸进程 孤儿进程 1 2 1 僵尸进程 1 2 2 孤儿进程 1 3 pread pwri
  • 前端-html-01

    1 前端开发 自学html css学习笔记 1 1 绝对路径 链接一张图的图片 img src 网页链接 本地连接 1 2 文本格式化 字体被 strong 加粗 strong 了 br 字体 em 倾斜 em br 我是
  • AI教程 如何在 Illustrator 中转换图稿?

    欢迎观看Illustrator教程 小编带大家学习 Illustrator 2022的基本工具和使用技巧 了解如何在 Illustrator 中应用旋转 反射和剪切转换图稿 在本文中我们将使用左侧的部件组装这枚火箭 并探索反射 剪切 旋转和
  • 使用python爬取豆瓣

    https www runoob com python python json html 我的理解就是告诉网站那边的人我是通过浏览器访问的 防止它分辨出我不是正常用户访问网站 这里要注意爬数据和调接口不是一回事 调接口我们是直接请求后端的数
  • [管理与领导-78]:IT基层管理者 - 核心技能 - 高效执行力 - 3 - 执行力的核心是拿到结果(ougput),而不是任务(input),也不是执行(过程、加工)

    前言 执行力的核心是结果 但并非所有人都理解 结果 对执行力的意义和作用 本文就是探讨对 结果 的理解 一 执行中常见的困惑 1 1 员工的困惑 接收任务 员工在执行工作过程中可能会遇到各种困惑 以下是一些可能的困惑情况 任务不清晰 员工可
  • 当 App 有了系统权限,真的可以为所欲为?

    看到群里发了两篇文章 出于好奇 想看看这些个 App 在利用系统漏洞获取系统权限之后 都干了什么事 于是就有了这篇文章 由于准备仓促 有些 Code 没有仔细看 感兴趣的同学可以自己去研究研究 多多讨论 深蓝洞察 2022 年度最 不可赦
  • GC 日志

    VM 参数 XX PrintGCDetails Xms30M Xmx30M Xmn10M 代码 import java lang management ManagementFactory import java lang managemen
  • log4j同步机制导致的cpu飙升排查与解决

    问题 组内某业务的几个相关接口均超时 上阿里云查日志一看是Dubbo调用超时 查看网络情况未发现异常 直接上Provider的机器查看占用 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 to
  • 两段锁(2PL)理解

    2019独角兽企业重金招聘Python工程师标准 gt gt gt 两段锁即两阶段锁 Two phase locking 2PL 首先 两阶段锁强调的是 加锁 增长阶段 growing phase 和解锁 缩减阶段 shrinking ph
  • 正割、余割、正弦、余弦、正切、余切之间的关系的公式 sec、csc与sin、cos、tan、cot之间的各种公式...

    1 倒数关系 tan cot 1 sin csc 1 cos sec 1 2 商数关系 tan sin cos cot cos sin 3 平方关系 sin cos 1 1 tan sec 1 cot csc 4 求导关系 sec sect
  • golang os/exec 执行终端命令

    1 只执行命令 不获取结果 直接调用 Cmd 对象的 Run 函数 返回的只有成功和失败 获取不到任何输出的结果 package main import log os exec func main cmd exec Command ls l
  • Kali Linux安装pip2

    问题说明 随着pytho3的强袭崛起 Kali 2020的版本去除了pip2 由于很多之前的黑客工具都是由python2写的 需要通过pip2来安装相应的模块 解决方法 Step 1 下载get pip py wget https boot
  • VSCODE中开发AE脚本环境搭建流程(Visual Studio Code for Adobe ExtendScript)

    VSCODE中开发AE脚本环境搭建流程 Visual Studio Code for Adobe ExtendScript 一 下载VSCODE 下载地址 https code visualstudio com 二 插件下载和配置 Adob
  • Qt中的布局浅析与弹簧的使用,以及Qt居中的两种方法

    1 布局 为什么要布局 布局之后窗口的排列是有序的 布局之后窗口的大小发生变化 控件的大小也会对应变化 如果不对控件布局 窗口显示出来之后有些控件的看不到的 布局是可以嵌套使用 常用的布局方式 水平布局 gt 所有的控件水平排列 gt 一行
  • 电子工程师必知必会——矢网史密斯Smith Chart圆图测试阻抗

    史密斯圆图是由很多圆周交织在一起的一个图 正确的使用它 可以在不作任何计算的前提下得到一个表面上看非常复杂的系统的匹配阻抗 唯一需要作的就是沿着圆周线读取并跟踪数据 本期视频贝贝就带大家学习矢网的史密斯圆图如何测试负载的阻抗 一起来学习吧
  • clickhouse代理chproxy安装部署实战最详细全网独一份

    安装chproxy 操作系统 centos7 X64 chproxy 是clickhouse官方列出的开源代理服务 go语言开发 https github com ContentSquare chproxy github下载地址 第1步下载
  • Springboot 多线程实现事务控制

    业务背景 工作中有很多 在一个service实现层 有可能调用其他的service 或者说是使用异步线程 这样就不容易控制当前操作成功和失败 不多说 上代码 获取数据库事务控制器 Component public class Transac