并发情况导致事务失效的场景

07-19 1391阅读

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方法,一个线程提交事务。不再会出现多线程同时提交导致数据被覆盖的情况了!

VPS购买请点击我

文章版权声明:除非注明,否则均为主机测评原创文章,转载或复制请以超链接形式并注明出处。

目录[+]