【多线程处理数据保证事务生效】

在异步执行的过程中,对数据库进行操作,同时确保在主线程和异步线程中捕获并处理异常,以保障事务的一致性。

[方法一]

public void a1() {
    try {
        studentMapper.insertA();

        CompletableFuture<Void> future = CompletableFuture.runAsync(() -> {
            // 获取当前线程的事务状态
            TransactionStatus subStatus = TransactionAspectSupport.currentTransactionStatus();
            try {
                b();
                c();
            } catch (Exception e) {
                System.err.println("线程异常");
                subStatus.setRollbackOnly();
                // 使用 completeExceptionally 将异常传播到 CompletableFuture 中
                CompletableFuture<Void> exceptionallyCompleted = new CompletableFuture<>();
                exceptionallyCompleted.completeExceptionally(e);
                throw new CompletionException(e);
            }
        });

        // 使用 join 方法等待异步任务的完成,并检查是否有异常
        future.join();
    } catch (Exception e) {
        System.err.println("主线程异常");
        TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
    }
}

如果b和c都是第三方接口,那就需要依赖返回的状态码去处理逻辑

public void a1() {
    try {
        studentMapper.insertA();

        CompletableFuture<Void> future = CompletableFuture.runAsync(() -> {
            // 获取当前线程的事务状态
            TransactionStatus subStatus = TransactionAspectSupport.currentTransactionStatus();
            try {
                if (!b()) {
                    throw new BusinessException("调用第三方服务b失败");
                }

                if (!c()) {
                    throw new BusinessException("调用第三方服务c失败");
                }
            } catch (BusinessException e) {
                System.err.println("线程异常: " + e.getMessage());
                subStatus.setRollbackOnly();
                throw new CompletionException(e);
            }
        });

        // 使用 join 方法等待异步任务的完成,并检查是否有异常
        future.join();
    } catch (Exception e) {
        System.err.println("主线程异常");
        TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
    }
}

private boolean b() {
    // 调用第三方服务b,根据返回值判断是否成功
    // 返回 true 表示成功,返回 false 表示失败
    return true; // or false
}

private boolean c() {
    // 调用第三方服务c,根据返回值判断是否成功
    // 返回 true 表示成功,返回 false 表示失败
    return true; // or false
}

[方法二]

public class BatchApplicationTest {
   @Autowired
   private UserMapper userMapper;
   @Autowired
   private PlatformTransactionManager transactionManager;

   @Test
   public void test() throws Exception {
       List<User> userList = buildUser();

       ExecutorService executorService = Executors.newFixedThreadPool(userList.size());
       CountDownLatch countDownLatch = new CountDownLatch(userList.size());
       //创建线程屏障对象,让所有线程执行完成后处于同一个等待状态,也可以使用
       CyclicBarrier cyclicBarrier = new CyclicBarrier(userList.size());

       int i = 0;
       //用于统计是否存在失败线程
       AtomicBoolean flag = new AtomicBoolean(true);
       for (; i < userList.size(); i++) {
           final Integer tmp = i;
           executorService.submit(() -> {
               TransactionStatus status = null;
               try {
                   DefaultTransactionDefinition def = new DefaultTransactionDefinition();
                   // 事物隔离级别,开启新事务,这样会比较安全些。
                   def.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRES_NEW); 
                   status = transactionManager.getTransaction(def); 

                     //随机设置一个线程失败并抛出异常;注释掉会全部完成,否则全部回滚
//                    if (tmp == userList.size() - 1) {
//                        throw new RuntimeException();
//                    }

                   // 具体的数据处理类
                   userMapper.insert(userList.get(tmp));
               } catch (Exception e) {
                  //如果出现异常就设置失败标志位
                   flag.set(false);
                   e.printStackTrace();
               } finally {
                   try {
                       //让所有线程都处于等待状态,不管成功还是失败
                       cyclicBarrier.await();

                       System.out.println("---" + flag.get());
                       //根据标志位判断事务最终执行状态
                       if (flag.get()) {
                           transactionManager.commit(status);
                       } else {
                           transactionManager.rollback(status);
                       }
                   } catch (InterruptedException e) {
                       e.printStackTrace();
                   } catch (BrokenBarrierException e) {
                       e.printStackTrace();
                   }
                   countDownLatch.countDown();
               }
           });
       }
       //主线程等待最终结果
       countDownLatch.await();
       System.out.println("--end--");
   }
}

方法一:使用 CompletableFuture 和 TransactionAspectSupport
优势:
简化事务管理: 利用 Spring 的 TransactionAspectSupport 简化了事务管理,通过捕获异常并设置回滚标志,避免了手动处理事务的复杂性。
异步处理: 使用 CompletableFuture 可以更方便地进行异步处理,通过 join 方法等待异步任务完成。
劣势:
固定的线程池: 使用 CompletableFuture.runAsync 默认使用 ForkJoinPool.commonPool(),线程池是固定的,不能灵活地控制线程数。
不可控的等待机制: 由于 CompletableFuture 不提供类似 CountDownLatch 的等待机制,可能需要额外的手段来实现主线程等待所有异步任务完成。
方法二:使用线程池和 CountDownLatch
优势:
粒度控制: 通过自定义线程池,可以更灵活地控制并发执行的线程数,适应不同的硬件资源和性能需求。
可控的等待机制: 使用 CountDownLatch 和 CyclicBarrier 可以实现主线程等待所有子线程完成后再继续执行的机制,使得主线程可以在所有子线程完成后进行一些后续操作。
劣势:
手动事务管理: 在每个线程中手动处理事务,包括开启新事务、提交或回滚事务。这增加了代码的复杂性,容易出错。
线程间通信: 使用共享的 AtomicBoolean 标志位进行线程间通信,可能存在并发问题,需要额外的注意和保护。
综合考虑:
如果对并发度有更灵活的要求,且需要更细粒度的线程控制,方法一可能更适合。
如果更注重简化事务管理,且异步处理更符合需求,方法二可能更适合。

  • 4
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
Java,如果你需要在多个数据执行同一事务,你可以使用分布式事务处理框架来实现。这些框架可以根据需要将多个数据库操作包装在同一个事务,并确保在所有操作完成之前,事务要么全部提交,要么全部回滚。 一种常见的分布式事务处理框架是Spring Framework。在Spring,你可以使用`@Transactional`注解来标记需要在同一事务执行的方法。在这种情况下,如果你使用了多个数据源,则需要使用JTA(Java Transaction API)来协调这些数据源。 具体地,你需要在应用程序配置一个JTA事务管理器,并在每个数据源上配置一个XA数据源。然后,你可以使用@Transactional注解来标记需要在同一事务执行的方法,Spring将自动使用JTA协调事务。 例如,以下示例演示了如何使用Spring和Atomikos JTA实现多线程数据保证同一事务: ```java @Service public class MyService { @Autowired private PlatformTransactionManager transactionManager; @Transactional public void doSomething() { // ... 执行数据库操作1 ... // ... 执行数据库操作2 ... } public void doSomethingInMultipleThreads() { Executor executor = Executors.newFixedThreadPool(10); for (int i = 0; i < 10; i++) { executor.execute(() -> { TransactionTemplate transactionTemplate = new TransactionTemplate(transactionManager); transactionTemplate.execute(status -> { // ... 执行数据库操作1 ... }); transactionTemplate.execute(status -> { // ... 执行数据库操作2 ... }); }); } } } ``` 在这个例子,`MyService`类的`doSomething()`方法和`doSomethingInMultipleThreads()`方法都被标记为@Transactional。`doSomething()`方法只是一个简单的事务方法,它执行两个数据库操作。`doSomethingInMultipleThreads()`方法则启动了10个线程,每个线程都执行相同的两个数据库操作。由于这些操作都在同一个事务执行,因此它们要么全部提交,要么全部回滚。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值