并发情况导致事务失效的场景
public void test(Pageable request){ for (int i = 0; i { userInfoService.testDemo(); }).start(); } }
这里创建多个线程模拟多并发场景
@Transactional(rollbackOn = Exception.class) public synchronized void testDemo() { UserInfo byUserId = userRepository.findByUserId(1); byUserId.setAge(byUserId.getAge() + 1); userRepository.save(byUserId); }
这里说一下我的理解:事务失效的原因
- 原因1:我们知道synchronized锁,锁的是调用当前方法的对象。而Spring AOP 处理事务会进行生成一个代理对象,并在代理对象执行方法前的事务开启,方法执行完的事务提交。因此这里每个线程都会创建一个代理对象,而synchronized锁的是这个代理对象,因此多少个线程就会有多少把锁,根本锁不住。所以说事务的执行,根本没有上锁。就会出现多个事务同时提交,导致数据被重复修改,结果达不到我们想要的情况(比如说这里我们修改了age字段100次,结果可能只有50)
- 原因2:因为Synchronized锁定的是当前调用方法对象,而Spring AOP 处理事务会进行生成一个代理对象,并在代理对象执行方法前的事务开启,方法执行完的事务提交,所以说,事务的开启和提交并不是在 Synchronized 锁定的范围内。出现同步锁失效的原因是:当A(线程) 执行完方法,会进行释放同步锁,去做提交事务,但在A(线程)还没有提交完事务之前,B(线程)获取锁进行执行方法,执行完毕之后和A(线程)一起提交事务, 这时候就会出现线程安全问题。
说明:为什么多线程一起提交事务就会导致数据丢失。
- 具体来说,假设你的事务是修改某一条数据记录的某个字段,如果A线程在修改完毕并提交事务之后,B线程再去修改这个字段并提交事务,这时候的流程是没有问题的。
- 但是,如果A线程刚完成修改,尚未提交事务,B线程此时也完成修改并试图提交事务,这时候就可能产生问题。因为在数据库中,提交事务通常意味着把修改写入数据库,如果两个线程同时提交事务,可能会导致两个线程前后冲突,数据被错误地覆盖。
这里粘一个gpt的回答
因此这个锁的范围,必须包含整个事务。
修改:
@GetMapping(path = "test") @ResponseBody public void test(Pageable request) { for (int i = 0; i { synchronized (UserController.class) { userInfoService.testDemo(); } }).start(); } }
这里synchronized锁整个UserController类。确保同一时间只有一个userInfoService调用testDemo方法。
@Transactional(rollbackOn = Exception.class) public void testDemo() { UserInfo byUserId = userRepository.findByUserId(1); log.info("当前线程:{},当前年龄:{}",Thread.currentThread().getName(),byUserId.getAge()); byUserId.setAge(byUserId.getAge() + 1); userRepository.save(byUserId); log.info("当前线程:{},当前年龄:{}",Thread.currentThread().getName(),byUserId.getAge()); }
如此一来,同一时间只有一个线程进入testDemo方法,一个线程提交事务。不再会出现多线程同时提交导致数据被覆盖的情况了!
文章版权声明:除非注明,否则均为主机测评原创文章,转载或复制请以超链接形式并注明出处。