绑定完请刷新页面
取消
刷新

分享好友

×
取消 复制
Synchronized 同步出现失效的问题你碰到过吗?
2019-12-23 01:08:10

Synchronized ,大家都知道这个是Java 提供的一种原子性内置锁,其实现原理是通过获取对象的监视器monitor进行来实现同步的,只有当线程获取到对象monitor才能继续执行,否则该线程进行阻塞(等待)。 示例:

public class DemoServiceImpl { 

	@Autowired    
	private DemoDao demoDao;    

	// 根据id获取序列号,获取完之后进行 + 1        
	@Transactional    
	public synchronized String getSn(Long id) {
	       DemoSn sn = demoDao.getSnById(id);       
	       sn.setSn(sn.getSn() + 1);       
	       demoDao.update(sn);         
	       return sn.getSn().toString();    
	}    

}
复制代码

描述一下代码示例: 该方法涉及++ 1 ,当如果多个线程进行调用该方法时,会出现数据安全性问题,所以我在这里进行加了 Synchronized, 保证该方法的每次执行只能允许一个线程进来。

本以为这里加了 Synchronized, 就是线程安全了,但是经过测试之后发现,这里同步锁并没有起作用,多个线程进行调用该方法时,还是出现返回的值重复。

先简单介绍一下Spring @Transactional执行原理: @Transactional 注解是Spring 提供来进行控制事务的注解,当注解标明在方法上时, 则会对 该方法进行做AOP 增强(动态代理),然后在方法执行前,开启事务,执行后提交事务。

如果上述示例方法只添加了 Synchronized, 并没有标明@Transactional, 因为加了同步锁,所以getSn() 一次只能允许一个线程进入, 执行过程:

A(线程): DemoServiceImpl#getSn() > B(线程): DemoServiceImpl#getSn() 如果上述示例方法添加了 Synchronized,并标明@Transactional 线程的执行过程:

A(线程): Spring begins transactional > DemoServiceImpl#getSn() > Spring commits transactional

B(线程): Spring begins transactional > DemoServiceImpl#getSn() > Spring commits transactional

这里看着是没有问题,开启事务 -> 执行方法体 -> 提交事务,但是这里也恰恰是出现Synchronized 失效的关键。

Synchronized 失效关键原因:是因为Synchronized锁定的是当前调用方法对象,而Spring AOP 处理事务会进行生成一个代理对象,并在代理对象执行方法前的事务开启,方法执行完的事务提交,所以说,事务的开启和提交并不是在 Synchronized 锁定的范围内。出现同步锁失效的原因是:当A(线程) 执行完getSn()方法,会进行释放同步锁,去做提交事务,但在A(线程)还没有提交完事务之前,B(线程)进行执行getSn() 方法,执行完毕之后和A(线程)一起提交事务, 这时候就会出现线程安全问题。

BLOG地址www.liangsonghua.com

关注微信公众号:松花皮蛋的黑板报,获取更多精彩!

公众号介绍:分享在京东工作的技术感悟,还有JAVA技术和业内佳实践,大部分都是务实的、能看懂的、可复现的


分享好友

分享这个小栈给你的朋友们,一起进步吧。

姜汁松花皮蛋是个啥菜?
创建时间:2020-06-11 11:13:16
分享工作中的技术感悟。
展开
订阅须知

• 所有用户可根据关注领域订阅专区或所有专区

• 付费订阅:虚拟交易,一经交易不退款;若特殊情况,可3日内客服咨询

• 专区发布评论属默认订阅所评论专区(除付费小栈外)

栈主、嘉宾

查看更多
  • tony0087
    栈主

小栈成员

查看更多
  • huijinrutu
  • xtepCool
戳我,来吐槽~