多线程学习----->导入工具类的事务测试

本文探讨了多线程环境下Spring事务管理的问题。在测试中发现,传统的Spring事务无法控制子线程中的事务,导致异常发生时不回滚。为确保单个线程的事务性,可以通过编程式事务管理手动回滚。然而,全局事务的实现仍面临挑战,特别是在多线程和多数据源场景下。总结指出,多线程虽然提高效率但引入了事务管理和数据一致性问题,需要权衡利弊并寻找合适的解决方案。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

多线程学习----->导入工具类的事务测试

工具类

工具类

public class ThreadPool3<T,K> {


    //单个线程处理的数据量
    private int singleCount;
    //处理的总数据量
    private int listSize;
    //开启的线程数
    private int runSize;
    //操作的数据集
    private List<T> list;
    //计数器
    private CountDownLatch begin,end;
    //线程池
    private ExecutorService executorService;
    //回调
    private CallBack<T,K> callBack;
    //每个线程的结果集
    List<Future<K>> result;

    public void setCallBack(CallBack<T,K> callBack) {
        this.callBack = callBack;
    }

    public ThreadPool3(int singleCount, List<T> list){
        this.singleCount = singleCount;
        this.list = list;
        if (list != null){
            this.listSize = list.size();
            this.runSize = (this.listSize/this.singleCount) + 1;
        }
        result = new ArrayList<>(  );
    }

    public List<Future<K>> excute() throws InterruptedException {
        executorService = Executors.newFixedThreadPool(runSize);
        begin = new CountDownLatch(1);
        end = new CountDownLatch(runSize);
        //创建线程
        int startIndex = 0;
        int endIndex = 0;
        List<T> newList = null;
        for (int i = 0; i < runSize; i++) {
            //计算每个线程对应的数据
            if (i < (runSize - 1)){
                startIndex = i * singleCount;
                endIndex = (i + 1) * singleCount;
                newList = list.subList(startIndex,endIndex);
            }else {
                startIndex = i * singleCount;
                endIndex = listSize;
                newList = list.subList(startIndex,endIndex);
            }
            //创建线程类处理数据
            MyThread<T,K> myThread = new MyThread<T,K>(newList, begin, end) {

                @Override
                public K method(List<T> list) {
                    return callBack.method( list );
                }
            };
            //执行线程
            Future<K> submit = executorService.submit( myThread );
            result.add( submit );
        }
        //计数器减一
        begin.countDown();
        end.await();
        //关闭线程池
        executorService.shutdown();
        return result;
    }

    //抽象线程类
    public abstract class MyThread<T,K> implements Callable<K> {

        private List<T> list;
        private CountDownLatch begin,end;

        public MyThread(List<T> list, CountDownLatch begin, CountDownLatch end){
            this.list = list;
            this.begin = begin;
            this.end = end;
        }

        @Override
        public K call() throws Exception {
            try {
                //执行程序
                K  k = method(list);
                begin.await();
                return k;
            } catch (InterruptedException e) {
                e.printStackTrace();
            }finally {
                //计数器减一
                end.countDown();
            }
            return null;
        }

        public abstract K method(List<T> list);
    }

    //回调接口定义
    public interface CallBack<T,K>{
        public K method(List<T> list);
    }
}

线程回调返回值的类

@Data
public class ThreadResult {

    private String threadName;

    private Integer code;

    private String  msg;
}

测试实体类

@Data
public class Pool {

    @TableId(type= IdType.AUTO)
    private Integer id;

    private String name;

    private Integer type = (int)new Random(  ).nextInt( 10 );
}

事务测试

1.传统spring事务能控制吗?

      @Override
    public void test() throws InterruptedException {
        long start = System.currentTimeMillis();
        List<Pool> list = new ArrayList<>();
        for (int i = 0; i < 10000; i++) {
            Pool pool = new Pool();
            pool.setName( "hello"+i );
            list.add(pool);
        }
        ThreadPool3<Pool, ThreadResult> threadPool3 = new ThreadPool3<>( 1000,list );
        threadPool3.setCallBack( new ThreadPool3.CallBack<Pool, ThreadResult>() {
            @Override
            public ThreadResult method(List<Pool> list) {
                ThreadResult threadResult = new ThreadResult();
                //数据处理
                try {
                    for (int i = 0; i < list.size(); i++) {
                        if (list.get( i ).getName().equals( "hello4789" )) {
                            int temp = 1 / 0;
                        }
                        poolRepository.insert( list.get( i ) );
                    }
                    threadResult.setCode( 200 );
                    threadResult.setMsg( null );
                    threadResult.setThreadName( Thread.currentThread().getName() );
                }catch (Exception e){
                    log.error( "线程:{},错误信息:{}",Thread.currentThread().getName(),e.getMessage() );
                   
                    threadResult.setCode( 500 );
                    threadResult.setMsg( e.getMessage() );
                    threadResult.setThreadName( Thread.currentThread().getName() );
                }
                return threadResult;
            }
        } );

        List<Future<ThreadResult>> excute = threadPool3.excute();
    }

我故意在name 这一块抛出一个异常,自己在捕获一下。正常按照事务的准则来说应该是这个子线程回滚或者主线程回滚,因为每个子线程数据都是规则排序好的,是但是经过查询数据来说,该子线程对应的数据hello4000 - hello4999,但是hello4789(包括在内)-hello4999的数据都是null,这就说明该子线程在抛出异常后没进行回滚,而是终止了这个线程

结论:主线程上家@Transactional 对子线程无效
原因:spring保证事务的原因是因为connection 是一个就可以设置事务,而每个线程的connection都是通过ThreadLocal保存的(我不知道InheritableThreadLocal有用吗)

2.保证单个线程的事务性
分析:上面也说了事务是针对线程的,那自己单个在抛出异常的时候手动回滚

   @Autowired
    private PlatformTransactionManager platformTransactionManager;

    @Autowired
    private TransactionDefinition transactionDefinition;
    
 TransactionStatus transactionStatus =  null;

 transactionStatus = platformTransactionManager.getTransaction(transactionDefinition);
 
 platformTransactionManager.commit(transactionStatus);

 platformTransactionManager.rollback(transactionStatus);

关键编程式事务回滚代码就是这几行了
再次测试后发现只有9000条数据了
说明那个子线程被回滚了

3.保证全局事务

我返回值记录了那个线程有问题,但是至于怎么全局回滚,还没找到方案。因为每个线程有自己的connection,无法控制10个线程在同一个connection。当然我知道如果通过控制connection来保证事务。
在这里插入图片描述

总结

其实我们用多线程解决问题其实就是拿空间换时间效率
利用线程池的好处有

  1. 减少资源的开销
  2. 方便统一管理

然而使用多线程也会遇到很多问题比如我现在想到的

  1. 如果保证全局事务
  2. 多数据源下事务问题
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值