今天发现了一段这样的代码,如下

@Transactional
    public void longlongAfter(){
        ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(4, 4, 0, TimeUnit.SECONDS, new LinkedBlockingDeque<>());
        threadPoolExecutor.execute(new Thread(){
            @Override
            public void run() {
                System.out.println("子线程...");
                for (int i = 0; i < 2; i++) {
                    User user1 = new User().setUserAge(i).setId(5l);
                    User user2 = new User().setUserAge(i).setId(6l);
                    userMapper.updateById(user1);
                    if (condition){
                        throw new RuntimeException("抛出业务异常");
                    }
                    userMapper.updateById(user2);
                }
            }
        });
    }

我们都知道Transactional 是创建了一个事务,保证方法的原子性,如果使用JDBC的写法就如下

相当于第一个代码块,将线程的创建启动放入了try语句块,但是子线程的运行并不会影响主线程,在thread.start开启子线程后,主线程会仍向下运行,主线程结束子线程并不会结束,所以这里的事务对子线程是没有任何约束力的。

 Connection connection = dataSource.getConnection();
        connection.setAutoCommit(false);
        Statement statement = connection.createStatement();
        try {
            statement.execute("insert into student values(null, '王五', 1)");
            int p = 1 / 0;
            statement.execute("insert into student_log values(null, '添加了个雪人')");
            System.out.println("sql 执行。。。");
            connection.commit();
        } catch (Exception e) {
            System.out.println("抛出异常 回滚");
            connection.rollback();
        }

上面的代码导致的问题
1、原子性被破坏,数据不正确。
2、在出现异常后,因为异常的抛出导致子线程提前结束,后续没有问题的反而没有进行处理

处理方式
如果需要关注线程的返回值,并且保证数据(db)的一致性,主线程则必须等待子线程的结束获取返回值,进行db数据处理,这里可以将循环拆分,开启多个线程同时进行处理,节省时间。就不再写代码了。

Logo

为开发者提供学习成长、分享交流、生态实践、资源工具等服务,帮助开发者快速成长。

更多推荐