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

相关推荐
章豪Mrrey nical7 小时前
前后端分离工作详解Detailed Explanation of Frontend-Backend Separation Work
后端·前端框架·状态模式
派大鑫wink8 小时前
【JAVA学习日志】SpringBoot 参数配置:从基础到实战,解锁灵活配置新姿势
java·spring boot·后端
程序员爱钓鱼8 小时前
Node.js 编程实战:文件读写操作
前端·后端·node.js
xUxIAOrUIII9 小时前
【Spring Boot】控制器Controller方法
java·spring boot·后端
Dolphin_Home9 小时前
从理论到实战:图结构在仓库关联业务中的落地(小白→中级,附完整代码)
java·spring boot·后端·spring cloud·database·广度优先·图搜索算法
zfj3219 小时前
go为什么设计成源码依赖,而不是二进制依赖
开发语言·后端·golang
weixin_462446239 小时前
使用 Go 实现 SSE 流式推送 + 打字机效果(模拟 Coze Chat)
开发语言·后端·golang
JIngJaneIL9 小时前
基于springboot + vue古城景区管理系统(源码+数据库+文档)
java·开发语言·前端·数据库·vue.js·spring boot·后端
小信啊啊10 小时前
Go语言切片slice
开发语言·后端·golang
Victor35611 小时前
Netty(20)如何实现基于Netty的WebSocket服务器?
后端