赞
踩
很多小伙伴使用Spring事务时,为了省事都喜欢使用@Transactional
。但是@Transactional
配合锁,会导致一些预期之外的问题!
在此举例说明。
我们将在该表中,实现level数据递减的并发操作。
Controller中,简单模拟10个线程各自执行10次:
- // service代码
- public void test() {
- // 简单的select + update 模拟业务场景
- Model model = mapper.choseOne("99");
-
- // 实现 level -- 操作
- Model updater = new Model();
- updater.setId("99");
- updater.setLevel(model.getLevel() - 1);
- mapper.updateOne(updater);
- }
执行结果:我们发现,level只扣减了26,说明存在并发问题!
- // service代码
- private Lock lock = new ReentrantLock();
-
- public void test() {
- try {
- //加锁
- lock.lock();
- // 简单的select + update 模拟业务场景
- Model model = mapper.choseOne("99");
-
- // 实现 level -- 操作
- Model updater = new Model();
- updater.setId("99");
- updater.setLevel(model.getLevel() - 1);
- mapper.updateOne(updater);
- } finally {
- lock.unlock(); // 解锁
- }
- }
执行结果:我们发现,使用锁是可以控制并发问题。
- // service代码
- private Lock lock = new ReentrantLock();
-
- @Transactional
- public void test() {
- try {
- //加锁
- lock.lock();
- // 简单的select + update 模拟业务场景
- Model model = mapper.choseOne("99");
-
- // 实现 level -- 操作
- Model updater = new Model();
- updater.setId("99");
- updater.setLevel(model.getLevel() - 1);
- mapper.updateOne(updater);
- } finally {
- lock.unlock(); // 解锁
- }
- }
执行结果:我们发现,level只扣减了86!用了@Transactional
之后,锁怎么就失效了呢!
我们都知道,@Transactional
是通过使用AOP,在目标方法执行前后进行事务的开启和提交。所以,Lock锁住的代码,其实并没有包含住一整个事务!
通过下面的图理解一下:
当线程A将level设置为99时,此时锁已经释放了,但是事务还没提交!!线程B此时可以获取到锁并进行查询,查询出来的level还是线程A修改之前的100,所以出现了并发问题。
- private Lock lock = new ReentrantLock();
- @Transactional
- public void test1() {
- // 简单的select + update 模拟业务场景
- Model model = mapper.choseOne("99");
-
- // 实现 level -- 操作
- Model updater = new Model();
- updater.setId("99");
- updater.setLevel(model.getLevel() - 1);
- mapper.updateOne(updater);
- }
-
- @Autowired
- @Lazy
- private CommonService commonService;
- public void test() {
- try {
- // 加锁
- lock.lock();
- // 自己注入自己,以使用到其代理类
- commonService.test1();
- } finally {
- lock.unlock(); // 解锁
- }
- }
执行结果:没有并发问题出现!
或者直接在controller层加锁,也是一样的道理。
- // service代码
- private Lock lock = new ReentrantLock();
- @Autowired
- private PlatformTransactionManager transactionManager;
- public void test() {
- try {
- //加锁
- lock.lock();
- // 编程式事务
- TransactionStatus status = transactionManager.getTransaction(new DefaultTransactionDefinition());
-
- // 简单的select + update 模拟业务场景
- Model model = mapper.choseOne("99");
-
- // 实现 level -- 操作
- Model updater = new Model();
- updater.setId("99");
- updater.setLevel(model.getLevel() - 1);
- mapper.updateOne(updater);
-
- // 在锁中提交
- transactionManager.commit(status);
- } finally {
- lock.unlock(); // 解锁
- }
- }
执行结果:我们发现,将整个事务都锁住,就没问题了!
最后说一句(求关注!别白嫖!)
如果这篇文章对您有所帮助,或者有所启发的话,求一键三连:点赞、转发、在看。
关注公众号:woniuxgg,在公众号中回复:笔记 就可以获得蜗牛为你精心准备的java实战语雀笔记,回复面试、开发手册、有超赞的粉丝福利!
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。