今天学习用ReentrantLock
锁的时候,遇到一个问题。对售出商品业务的代码加上该锁。
@Service
public class ServiceOne{
// 设置一把可重入的公平锁
private Lock lock = new ReentrantLock(true);
@Transactional(rollbackFor = Exception.class)
public Result func(long seckillId, long userId) {
lock.lock();
// 执行数据库操作——查询商品库存数量
// 如果 库存数量 满足要求 执行数据库操作——减少库存数量——模拟卖出货物操作
lock.unlock();
}
}
前提是使用MySQL数据库
的可重复读
隔离机制。如果是高并发的情况下,假设真的就有多个线程同时调用该方法。那么当事务的开启与提交能完整的包裹在lock
与unlock
之间时,那么就不会出现超卖的问题。显然事务的开启一定是在lock
之后的,故关键在于事务的提交
是否一定在unlock
之前。
如果在unlock
之后,那么是真的有可能发生超卖的:比如线程一执行该方法完毕但事务却还没提交时,线程二获得锁也开始执行该方法,在可重复读的
隔离机制下,线程二是读不到线程一对库存的操作结果的,从而超卖。
希望大家能够前来解惑答疑!感激不尽!
上面两位同学已经解答得很好了,我再来补充一下答案,这个问题的解决使自己受益匪浅。
在StackOverFlow上有人这么回答:
Your method is not only what actually is executed by Spring. Spring uses proxies, so as you can quess for your class and for your method a proxy is created as well.
As already commented
@Transactional
is implemented using the aspectaround
meaning some code before your method's execution and some code after your method's execution is executed as well.So if you have written
What the proxy of spring may actually seem like will be
意思就是说,事务确实是在方法结束时提交的。
今天(2021/11/10)思否给我颁发了一个火爆问题的徽章,没错就是这个问题带来了这个徽章。在这个问题提出之后的日子又学习了一些新的相关知识,更加使我坚信这个回答的正确性。
也正因此,我想做些许补充。由于这里的事务是由
Spring
带来并管理的,那么了解这个原理就对该问题的分析大有裨益。实际上Spring
采用一种动态代理的方式来对加了@Transactional
注解的方法进行增强(即在该方法执行前,添加事务,并在方法执行完成后提交事务。)。因此,正如上面回答的一样,事务确实是在该方法结束时提交的。那怎么处理呢?其实也很简单。
method
方法就是带注解的方法,这样就能用锁包裹住事务了。当然我们一定要注意这个事务是否能生效,关于事务是否能生效的相关文章也写过,在我历史文章中就能找到!-- 完结!!!