springboot实现基于多线程@Async的事务统一管理


前言

随着业务的不断深入,总有一些接口需要操作数据库中的很多表,前阵子公司的一个新增功能,就牵扯到了6、7个表的新增,而且其中还要对数据进行一些处理,测试了一下这个接口完成所有操作需要4s,这就不得不值得深思,通过网上查阅以及学习发现了个java提供的CountDownLatch工具类。实现的思想是基于一个分布式事务的解决方案2pc


一、CountDownLatch是什么?

一个同步辅助类,在完成一组正在其他线程中执行的操作之前,它允许一个或多个线程一直等待。

用给定的计数 初始化 CountDownLatch。由于调用了 countDown() 方法,所以在当前计数到达零之前,await 方法会一直受阻塞。之后,会释放所有等待的线程,await 的所有后续调用都将立即返回。这种现象只出现一次——计数无法被重置。如果需要重置计数,请考虑使用 CyclicBarrier。

CountDownLatch 是一个通用同步工具,它有很多用途。将计数 1 初始化的 CountDownLatch 用作一个简单的开/关锁存器,或入口:在通过调用 countDown() 的线程打开入口前,所有调用 await 的线程都一直在入口处等待。用 N 初始化的 CountDownLatch 可以使一个线程在 N 个线程完成某项操作之前一直等待,或者使其在某项操作完成 N 次之前一直等待。

CountDownLatch 的一个有用特性是,它不要求调用 countDown 方法的线程等到计数到达零时才继续,而在所有线程都能通过之前,它只是阻止任何线程继续通过一个 await。

二、使用步骤

1.

代码如下(示例):

	@Transactional(rollbackFor = Exception.class)
    @Override
    public void addDesignSpu(DesignSpuAddDTO designSpuAddDTO) {
        LoginAppUser currentUser = userService.getCurrentUser();
        Long tenantId = currentUser.getTenantId();
        checkAddDTO(designSpuAddDTO, tenantId);
        // 主线程的新增因为每个子线程都需要这个数据,所以放到了主线程里
        DesignSpu designSpu = addSpu(designSpuAddDTO, currentUser, tenantId);
        CountDownLatch countDownLatch = new CountDownLatch(1);
        CountDownLatch mainDownLatch = new CountDownLatch(4);// 4是代表子线程的数量
        // 用来记录子线程的运行状态,只要有一个失败就变为true
        AtomicBoolean rollBackFlag = new AtomicBoolean(false);
        // 用来存每个子线程的异常,把每个线程的自定义异常向vector的首位置插入,其余异常向末位置插入,避免线程不安全,所以使用vector代替list
        Vector<BusinessException> exceptionVector = new Vector<>();
        // 第一个子线程
        purchaseStatusChangeThread.addSku(countDownLatch, mainDownLatch, rollBackFlag, exceptionVector, designSpuAddDTO, designSpu, currentUser, tenantId);
        HashMap<String, List<DesignPlan>> hashMap = new HashMap<>();// 用来获取子线程的返回值
        // 第二个子线程
        purchaseStatusChangeThread.addPosition(countDownLatch, mainDownLatch, rollBackFlag, exceptionVector, hashMap, designSpuAddDTO, designSpu, currentUser, tenantId);
        // 第三个子线程
        purchaseStatusChangeThread.addDesignProcess(countDownLatch, mainDownLatch, rollBackFlag, exceptionVector, designSpuAddDTO, designSpu, currentUser);
        // 第四个子线程
        purchaseStatusChangeThread.addSewing(countDownLatch, mainDownLatch, rollBackFlag, exceptionVector, designSpuAddDTO, designSpu, currentUser, tenantId);
        if (!rollBackFlag.get()) {
            try {
            	// mainDownLatch等待,直到所有子线程执行完插入操作,但此时还没有提交事务
                mainDownLatch.await();
                countDownLatch.countDown();// 根据rollBackFlag状态放行子线程的await处,告知是回滚还是提交
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        if (CollectionUtils.isNotEmpty(exceptionVector)) {
            throw exceptionVector.get(0);
        }
        // 以下是业务代码返回给前端的数据
        jsonObject.put("designSpuId", designSpu.getId());
        jsonObject.put("planList", hashMap.get("planList"));
        return jsonObject;
    }

2.purchaseStatusChangeThread线程类里的代码

代码太多,此处只以第二个子线程的代码为例,其余的同理:

	@Async("taskExecutor")
    public void addPosition(CountDownLatch countDownLatch, CountDownLatch mainDownLatch, AtomicBoolean atomicBoolean, Vector<BusinessException> exceptionVector, HashMap<String, List<DesignPlan>> hashMap, DesignSpuAddDTO designSpuAddDTO, DesignSpu designSpu, LoginAppUser currentUser, Long tenantId) {
    // 如果这时有一个子线程已经出错,那当前线程不需要执行
        if (atomicBoolean.get()) {
            mainDownLatch.countDown();
            return;
        }
        HashMap<String, Long> positionMap = new HashMap<>();
        DefaultTransactionDefinition def = new DefaultTransactionDefinition();// 开启事务
        def.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRES_NEW);// 设置事务隔离级别
        TransactionStatus status = transactionManager.getTransaction(def);
        try {
           	// 业务代码…………
           	// ………………
           	// ………………
            // 截止到这里上面都是业务
            mainDownLatch.countDown();// 对countDownLatch-1
            countDownLatch.await();// 如果countDownLatch不是0,线程会在此阻塞,直到countDownLatch变为0
            // 如果能执行到这一步说明所有子线程都已经执行完毕判断如果atomicBoolean是true就回滚false就提交
            if (atomicBoolean.get()) {
                transactionManager.rollback(status);
            } else {
                transactionManager.commit(status);
            }
        } catch (Exception e) {
        	// 此处代表出现异常,把异常放到主线程的vector里,然后由主线程抛给前端
            if (e instanceof BusinessException) {
                exceptionVector.add(0, (BusinessException) e);
            } else {
                exceptionVector.add(CollectionUtils.isEmpty(exceptionVector) ? 0 : exceptionVector.size() - 1, new BusinessException(CommonErrorCode.UNKNOWN));
            }
            e.printStackTrace();
            // 并把状态设置为true
            atomicBoolean.set(true);
            countDownLatch.countDown();
            mainDownLatch.countDown();
            // 回滚
            transactionManager.rollback(status);
        }
    }

总结

此方案类似于分布式事务的实现原理2pc,主线程当做协调者,各子线程作为参与者,只用一个@Transactional注解肯定不能实现多线程的事务控制。

评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值