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) { /* 业务代码在该区域 */ }