java中锁与@Transactional同时使用导致锁失效的问题

示例代码

  @Transactional
    public void update(int id) {
        boolean lock = redisLock.lock(id);
        if (!lock) {
            throw new RuntimeException("当前人数过多,请稍后再试");
        }
        /*
          业务代码在该区域
         */
        redisLock.unlock(id);
    }

在上面的代码中,我们同时使用了@transactional和redis分布式锁(其他锁同理,比如synchronized同步锁也会出现这个问题)

问题分析

  上面这个例子是无法保证数据的一致性.由于spring的aop,会在update方法之前开启事务,之后再加锁,当锁住的代码执行完成后,再提交事务,因此锁住的代码块执行是在事务之内执行的,可以推断在代码块执行完时,事务还未提交,锁已经被释放,此时其他线程拿到锁之后进行锁住的代码块,读取的库存数据不是最新的。

解决方法

  我们可以在update方法之前就加上锁,在还没有开事务之前就加锁,那么就可以保证线程的安全性,从而不会出现脏读和数据不一致性等情况.

   @RequestMapping("/execute")
    public void execute(int id) {
        boolean lock = redisLock.lock(id);
        if (!lock) {
            throw new RuntimeException("当前人数过多,请稍后再试");
        }
        service.update(id);
        redisLock.unlock(id);
    }
   @Transactional
    public void update(int id) {
        /*
          业务代码在该区域
         */
    }
posted @ 2019-05-11 03:37  宇宙间的星河集  阅读(3105)  评论(0编辑  收藏  举报