JAVA多线程事务演示方案

简介

一般业务处理放置在service层处理的数据库操作,我们都会使用spring事务进行管理,可是当我们使用多线程时候,线程脱离spring管理,因此是无法进行事务管理的,只能手动管理事务。

该Demo演示如何手动管理多线程事务。
码云Demo地址:https://gitee.com/zhaojiyuan/thread-transactional-demo

关键代码

添加任务
/**
   * 添加要异步执行的方法程序
   *
   * @param supplier 任务
   */
  public void addFunction(Supplier supplier) {
    supplierList.add(supplier);
  }
执行队列中的任务
/**
   * 执行队列中的任务
   */
  public Boolean execute() {
    if (supplierList.size() > THREAD_COUNT_WARN) {
      log.warn("当前线程数为:{}个,线程数大于数据库连接数将发生死锁");
    }
    // 创建一个线程池
    executorService = Executors.newFixedThreadPool(supplierList.size());
    // 初始化 线程 计数器
    countDownLatch = new CountDownLatch(supplierList.size());
    // 提交任务
    supplierList.forEach(it -> executorService.submit(new TransactionRunnable(platformTransactionManager, it)));

    try {
      // 主线程 等待计数器为 0 时 进行提交或回滚
      countDownLatch.await();
      if (isError.get()) {
        log.error("多线程执行失败");
        return false;
      }
    } catch (InterruptedException e) {
      // 打印异常日志
      log.error("多线程执行失败" + e.getMessage());
      return false;
    }
    return true;
  }
多线程任务执行
/**
   * 实现 Runnable 接口
   */
  class TransactionRunnable implements Runnable {

    /**
     * 事务管理器
     */
    private PlatformTransactionManager platformTransactionManager;
    /**
     * 当前任务
     */
    private Supplier supplier;

    /**
     * 构造函数
     *
     * @param platformTransactionManager 事务管理器
     * @param supplier                   当前任务
     */
    public TransactionRunnable(PlatformTransactionManager platformTransactionManager, Supplier supplier) {
      this.platformTransactionManager = platformTransactionManager;
      this.supplier = supplier;
    }

    @Override
    public void run() {
      log.debug("子线程执行:开始");
      DefaultTransactionDefinition def = new DefaultTransactionDefinition();
      // 创建当前线任务的事务
      def.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRES_NEW);
      TransactionStatus transaction = this.platformTransactionManager.getTransaction(def);
      log.debug("子线程执行:开始");
      try {
        // 尝试获取任务值
        supplier.get();
      } catch (Exception e) {
        // 异常时,设置错误标记
        isError.set(true);
        log.error("子线程执行:异常{}", e.getMessage());
      }
      // 线程计数器 -1
      countDownLatch.countDown();
      try {
        // 子线程 等待计数器为 0 时 进行提交或回滚
        countDownLatch.await();
        if (isError.get()) {
          //事务回滚
          platformTransactionManager.rollback(transaction);
          log.info("子线程执行:事务回滚");
        } else {
          //事务提交
          platformTransactionManager.commit(transaction);
          log.info("子线程执行:事务提交");
        }
      } catch (InterruptedException e) {
        e.printStackTrace();
      }
    }
  }
调用示例
// 定义此主线程的 异步处理时事务管理
    MultiThreadTransactionComponent mt = new MultiThreadTransactionComponent(dataSourceTransactionManager);

    // 创建10个任务,插入到数据库中
    IntStream.range(0, 10).forEach(it -> {
      mt.addFunction(() -> {
        testMapper.insertTestData(it + "");

        // 模拟发生异常
        if (it == 9) {
          int i = 1 / 0;
        }
        return 0;
      });
    });

    // 执行任务
    mt.execute();

测试

通过http请求调用controller进行测试:
http://localhost:8801/springboot/base/test/threadTransactionalTest

注意事项

  1. 因为事务需要所有线程都执行完成之后一块提交,因此无法使用线程池管理线程,因为当任务数量高于线程池核心任务数后,没有提交事务的线程会一直占用线程,所以剩余未执行的任务永远等待可用线程造成死锁。当任务数量小于核心任务数时候,也没有必要使用线程池。
  2. 请确认任务数量一定要小于项目数据库连接池数量,要不然未提交的事务会一直占用数据库连接,新的任务无法获取到数据库连接,造成死锁。
  3. 综合以上原因,建议尽量少用、慎用多线程处理数据库操作。我也在学习中,欢迎交流,共同学习。
  4. 最新代码以码云为准

致谢

参考【阳宗德】的博客:手动事务及多线程事务
参考地址:https://blog.csdn.net/qq_35385687/article/details/116978028

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值