Springboot多线程操作事务

代码实例gitee.com/ZHANGBANFAN...

注意修改数据库连接,会自动创建bf_thread_transactional数据库和aa、bb两张表,在test文件夹下有测试用例

核心逻辑就是:划分子任务->子线程执行->主线程等待所有子线程完成->通知所有子线程提交或回滚。

其中的子线程返回主线程,主线程等待子线程等待都是通过CountDownLatch实现,可以获取代码详细体验一下

主任务处理

scss 复制代码
/**
 * 多线程操作数据库
 */
@Data
@Slf4j
public class ThreadTransactionalUtils {
    /**
     * 存放子任务
     */
    CopyOnWriteArrayList<CommonService> jobList = new CopyOnWriteArrayList<>();

    /**
     * 表示原始方法jobList是携带了事务
     */
    CopyOnWriteArrayList<CommonService> jobPrimitiveList = new CopyOnWriteArrayList<>();


    /**
     * 存放子任务名称
     */
    CopyOnWriteArrayList<String> jobNameList = new CopyOnWriteArrayList<>();


    /**
     * 任务名称
     */
    private String jobName;

    public ThreadTransactionalUtils(String jobName) {
        this.jobName = jobName;
    }


    @SneakyThrows
    public Boolean runConsumers() {
        log.info("==》无返回,多线程,springboot本地事务,操作数据库开始,任务名称:{}", jobName);
        // 获取线程池
        ExecutorService threadPool = Executors.newFixedThreadPool(5);
        // 每个子线程最大操作时间
        long timeout = 60L;
        // 等待子线程提交或者回滚的时间
        long endTimeout = 120L;
        // 为了让每个线程执行完后回到当前线程,使用CountDownLatch,值为线程数,每次线程执行完就会执行countDown方法减1,为0后回到主线程,也就是当前线程执行后续的代码
        int threadSize = jobList.size();
        CountDownLatch count = new CountDownLatch(threadSize);

        // 用来控制主线程回到子线程
        CountDownLatch mainCount = new CountDownLatch(1);

        // 用来控制最终回到主线程
        CountDownLatch endCount = new CountDownLatch(threadSize);

        // 用来存放子线程的处理结果,若出错就保存一个false
        CopyOnWriteArrayList<Boolean> sonResult = new CopyOnWriteArrayList<Boolean>();

        // 使用线程安全的对象存储,保存主线程最后总的判断结果,是提交还是回滚
        AtomicBoolean ifSubmit = new AtomicBoolean(true);
        // 保存子线程异常
        AtomicReference<Exception> exception = new AtomicReference<Exception>();
        // 添加任务
        for (int i = 0; i < threadSize; i++) {
            ThreadTransactionalUtilsItem thread = new ThreadTransactionalUtilsItem(jobList.get(i), count, sonResult,
                mainCount, ifSubmit, endCount, jobNameList.get(i), exception);
            // 执行方法
            threadPool.execute(thread);
        }

        try {
            // 设置60s超时
            boolean allTasksCompleted = count.await(timeout, TimeUnit.SECONDS);
            if (!allTasksCompleted) {
                log.warn("主线程:任务执行超时!");
                // 处理超时情况,设为回滚
                ifSubmit.set(false);
            } else {
                for (Boolean resp : sonResult) {
                    if (!resp) {
                        log.info("主线程:有线程执行失败,所有线程需要回滚");
                        ifSubmit.set(false);
                        break;
                    }
                }
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            // 回到子线程处理回滚或者提交事务
            mainCount.countDown();
        }
        try {
            // 设置超时,等待子线程处理事务,设为 60 秒
            if (!endCount.await(endTimeout, TimeUnit.SECONDS)) {
                log.warn("主线程:事务提交或回滚超时!");
                ifSubmit.set(false);
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        Exception exception1 = exception.get();
        // 如果是自定义的异常,抛出
        if (!ifSubmit.get() && exception1 instanceof BfException) {
            throw new BfException(exception1.getMessage());
        }
        return ifSubmit.get();
    }

    /**
     * 添加任务
     */
    public ThreadTransactionalUtils addConsumers(CommonService f, String jobName) {
        // 给每个方法添加事务
        PlatformTransactionManager transactionManager = SpringUtils.getBean(PlatformTransactionManager.class);
        jobList.add(() -> {
            // 设置公共属性
            DefaultTransactionDefinition def1 = new DefaultTransactionDefinition();
            def1.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED);
            // 设置隔离级别为SERIALIZABLE
            def1.setIsolationLevel(TransactionDefinition.ISOLATION_READ_COMMITTED);
            transactionManager.getTransaction(def1);
            f.accept();
        });
        jobPrimitiveList.add(f);
        jobNameList.add(jobName);
        return this;
    }
}

注意 ExecutorService threadPool = Executors.newFixedThreadPool(5);可以修改为自定义的线程池 以及相关超时时间 子任务处理

csharp 复制代码
@Slf4j
public class ThreadTransactionalUtilsItem implements Runnable {

    /**
     * 记录子线程第一次执行是否完成
     */
    private CountDownLatch count;

    /**
     * 保存每个线程的执行结果
     */
    private CopyOnWriteArrayList<Boolean> sonResult;

    /**
     * 记录主线程是否执行过判断每个线程的执行结果这个操作
     */
    private CountDownLatch mainCount;

    /**
     * 记录主线程对每个线程的执行结果的判断
     */
    private AtomicBoolean ifSubmit;

    /**
     * 声明该子线程的事务管理器
     */
    private DataSourceTransactionManager dataSourceTransactionManager;

    /**
     * 声明该线程事务的状态
     */
    private TransactionStatus status;

    /**
     * 记录子线程第二次执行是否完成
     */
    private CountDownLatch endCount;

    /**
     * 需要执行的方法
     */
    private CommonService commonService;


    /**
     * 子任务名称
     */
    private String jobName;

    private AtomicReference<Exception> exception;

    public ThreadTransactionalUtilsItem(CommonService commonService, CountDownLatch count, CopyOnWriteArrayList<Boolean> sonResult,
                                        CountDownLatch mainCount, AtomicBoolean ifSubmit, CountDownLatch endCount, String jobName, AtomicReference<Exception> exception) {
        this.commonService = commonService;
        this.count = count;
        this.sonResult = sonResult;
        this.mainCount = mainCount;
        this.ifSubmit = ifSubmit;
        this.endCount = endCount;
        this.jobName = jobName;
        this.exception = exception;
    }

    @Override
    public void run() {
        try {
            dataSourceTransactionManager = SpringUtils.getBean(DataSourceTransactionManager.class);
            DefaultTransactionDefinition def = new DefaultTransactionDefinition();
            def.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRES_NEW);
            status = dataSourceTransactionManager.getTransaction(def);
            commonService.accept();
            sonResult.add(true);
        } catch (Exception e) {
            e.printStackTrace();
            sonResult.add(false);
            exception.set(e);
            log.info("==>任务:{},执行出现异常!", jobName);
        } finally {
            // 当一个线程执行完了计数要减一不然这个线程会被一直挂起
            count.countDown();
            try {
                log.info("==>{}:准备就绪,任务名称:{},等待其他线程结果,判断是否事务提交", Thread.currentThread().getName(), jobName);
                mainCount.await();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            try {
                if (ifSubmit.get()) {
                    dataSourceTransactionManager.commit(status);
                    log.info("==>{}:事务提交,任务名称:{}", Thread.currentThread().getName(), jobName);
                } else {
                    dataSourceTransactionManager.rollback(status);
                    log.info("==>{}:事务回滚,任务名称:{}", Thread.currentThread().getName(), jobName);
                }
            } catch (Exception e) {
                e.printStackTrace();
            }finally {
                // 执行完所有逻辑,等待主线程执行
                endCount.countDown();
            }
        }
    }
}

参考连接 cloud.tencent.com/developer/a...

相关推荐
几颗流星2 小时前
Rust 常用语法速记 - 错误处理
后端·rust
lypzcgf2 小时前
Coze源码分析-资源库-创建知识库-后端源码-应用/领域/数据访问
后端·go·coze·coze源码分析·智能体平台·ai应用平台·agent平台
LaoZhangAI2 小时前
Google Gemini AI图片编辑完全指南:50+中英对照提示词与批量处理教程(2025年9月)
前端·后端
小枫编程2 小时前
Spring Boot 调度任务在分布式环境下的坑:任务重复执行与一致性保证
spring boot·分布式·后端
用户11481867894843 小时前
从零搭建 Vue3 + Nest.js 实时通信项目:4 种方案(短轮询 / 长轮询 / SSE/WebSocket)
前端·后端
LaoZhangAI3 小时前
Google Gemini Nano与Banana AI完整部署指南:2025年轻量级AI解决方案
前端·后端
Java水解3 小时前
spring中的@SpringBootTest注解详解
spring boot·后端
似水流年流不尽思念3 小时前
Java线程状态转换的详细过程
后端
尚学教辅学习资料3 小时前
基于Spring Boot的家政服务管理系统+论文示例参考
java·spring boot·后端·java毕设