今天发现了一段这样的代码,如下
@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数据处理,这里可以将循环拆分,开启多个线程同时进行处理,节省时间。就不再写代码了。