多线程保证单个线程开启事务并生效的方案

我们开发的时候常常会遇到多线程事务的问题。以为添加了@Transactional注解就行了,其实你加了注解之后会发现事务失效。原因:数据库连接spring是放在threadLocal里面,多线程场景下,拿到的数据库连接是不一样的,即是属于不同事务。

 Spring支持编程式事务和声明式事务,Spring提供的最原始的事务管理方式是基于TransactionDefinition、PlatformTransactionManager、TransactionStatus 编程式事务。
TransactionTemplate的事务管理是使用模板方法设计模式对原始事务管理方式的封装,因此我们也可以用TransactionTemplate管理事务。

今天我们就来谈下单个线程开启事务的方案:

1. 直接把线程方法单独提出,添加@Transactional注解

// 开启多线程的方法
public void testTransactional() throws ExecutionException, InterruptedException {
        int inter = 3;
        ExecutorService executorService = Executors.newFixedThreadPool(inter);
        ThreadPoolExecutor poolExecutor = (ThreadPoolExecutor) executorService;
        FutureTask[] integerFuture = new FutureTask[inter];
        for (int i = 0; i < inter; i++) {
            int finalI = i + 1;
            integerFuture[i] = new FutureTask<>(() -> {
                testTransactionalServiceZi.testTransactionalZi(finalI, "计算" + finalI);
                System.out.println("多线程运行了---" + finalI);
                return "业务执行成功";
            });
            poolExecutor.execute(integerFuture[i]);

        }
        for (int i = 0; i < inter; i++) {
                System.out.println("integerFuture返回结果 = " + i + "==" + integerFuture[i].get());
        }
    }



    // 单个线程调用,添加注解
    @Transactional
    public void testTransactionalZi(int num, String str) {
        // 修改数据===1===
        AdCourse adCourse = new AdCourse();
        adCourse.setId(num);
        adCourse.setName(str);
        int i = adCourseMapper.updateById(adCourse);

        // 修改数据===2===
        adCourse.setId(num + 3);
        adCourse.setName(str + 0);
        int result = adCourseMapper.updateById(adCourse);

        // 手动制造异常(事务会生效)
        result = 1/0;
    }

2. 用TransactionTemplate管理事务

通过对源码的解读,TransactionTemplate封装的是PlatformTransactionManager。我们自己手写PlatformTransactionManager管理事务比较麻烦,所以通过TransactionTemplate来进行展示代码,如下:

@Service
public class TestTransactionalServiceImpl implements TestTransactionalService {

    @Autowired
    private TestTransactionalServiceZi testTransactionalServiceZi;

    // 使用Template控制事务
    @Autowired
    private TransactionTemplate template;

    public void testTransactional() throws ExecutionException, InterruptedException {
        int inter = 3;
        ExecutorService executorService = Executors.newFixedThreadPool(inter);
        ThreadPoolExecutor poolExecutor = (ThreadPoolExecutor) executorService;
        FutureTask[] integerFuture = new FutureTask[inter];
        for (int i = 0; i < inter; i++) {
            int finalI = i + 1;
            integerFuture[i] = new FutureTask<>(() -> {
                return template.execute(new TransactionCallback<String>() {
                    @Override
                    public String doInTransaction(TransactionStatus transactionStatus) {
                        testTransactionalServiceZi.testTransactionalZi(finalI, "计算666" + finalI);
                        System.out.println("多线程运行了---" + finalI);
                        return "业务执行成功";
                    }
                });
            });
            poolExecutor.execute(integerFuture[i]);
        }
        for (int i = 0; i < inter; i++) {
                System.out.println("integerFuture返回结果 = " + i + "==" + integerFuture[i].get());
        }
    }
}


    // 需要调用的方法
    public void testTransactionalZi(int num, String str) {
        // 修改数据===1===
        AdCourse adCourse = new AdCourse();
        adCourse.setId(num);
        adCourse.setName(str);
        int i = adCourseMapper.updateById(adCourse);

        // 修改数据===2===
        adCourse.setId(num + 3);
        adCourse.setName(str + 0);
        int result = adCourseMapper.updateById(adCourse);

        // 手动制造异常(事务会生效)
        result = 1/(num-1);
    }

打印结果:第一个线程没有执行成功,出现了异常,数据也没有改变。

 总结:以上两种方法可以保证多线程中单个线程事务的问题

  • 4
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值