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...

相关推荐
码事漫谈18 小时前
智能体颠覆教育行业调研报告:英语、编程、语文、数学学科应用分析
后端
蓝-萧18 小时前
使用Docker构建Node.js应用的详细指南
java·后端
码事漫谈19 小时前
《C语言点滴》——笑着入门,扎实成长
后端
Tony Bai19 小时前
【Go模块构建与依赖管理】09 企业级实践:私有仓库与私有 Proxy
开发语言·后端·golang
咖啡教室20 小时前
每日一个计算机小知识:ICMP
后端·网络协议
间彧20 小时前
OpenStack在混合云架构中通常扮演什么角色?
后端
咖啡教室20 小时前
每日一个计算机小知识:IGMP
后端·网络协议
间彧20 小时前
云原生技术栈中的核心组件(如Kubernetes、Docker)具体是如何协同工作的?
后端
清空mega20 小时前
从零开始搭建 flask 博客实验(3)
后端·python·flask
努力的小郑20 小时前
Elasticsearch 避坑指南:我在项目中总结的 14 条实用经验
后端·elasticsearch·性能优化