Synchronized 同步出现失效

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, 就是线程安全了,但是经过测试之后发现,这里同步锁并没有起作用,多个线程进行调用该方法时,还是出现返回的 值重复。

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(线程)一起提交事务, 这时候就会出现线程安全问题。

作者:技术基地
链接:https://www.jianshu.com/p/3e30cc705f09
来源:简书
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

Synchronized 失效原因的更多相关文章

  1. oracle 索引失效原因及解决方法

    oracle 索引失效原因及解决方法 2010年11月26日 星期五 17:10 一.以下的方法会引起索引失效 ‍1,<>2,单独的>,<,(有时会用到,有时不会)3,like ...

  2. 清除浮动元素的margin-top失效原因(更改之前的错误)

    //样式代码body,div{ margin:; padding:; } .box1{ background:#900; width:200px; height:200px; margin:20px ...

  3. 高阶篇:4.2.2)DFMEA层级分明的失效模式、失效后果、失效原因

    本章目的:明确失效模式.失效后果.失效原因的定义,分清楚层次关系,完成DFMEA这部分的填写. 1.失效模式,失效后果,失效原因的定义: 这是FEMEA手册第四册中的定义. 1.1 潜在失效模式 (b ...

  4. kubectl get 后按2次tab键命令补全的失效原因分析

    kubectl get 后按2次tab键命令补全的失效原因分析 2019/10/28 Chenxin a.bash客户端工具 在centos用户下, cd ~;echo "source &l ...

  5. z-index失效原因分析——由一个bug引发的对层叠上下文和z-index属性的深度思考

    新年刚开工就被一个bug虐得整个人都不好了,特地记录下. (一)bug描述 在一个fixed-data-table(一个React组件)制作的表格中,需要给表头的字段提示的特效,所以做了一个提示层,但 ...

  6. 【转】Android中removeCallbacks失效原因

    原文网址:http://blog.sina.com.cn/s/blog_6714fba70100wtx1.html 在Android开发中会使用Handle的removeCallbacks函数,该函数 ...

  7. Oracle索引失效原因及解决方法

    一.Oracle索引失效的原因 1使用否定关键字 !=, <> ,not in,not exist select * fromdrama where id <> 1,Mysql ...

  8. spring管理hibernate,mybatis,一级缓存失效原因

    mybatis缓存:一级缓存和二级缓存 hibernate缓存:一级缓存和二级缓存 关于缓存: 缓存是介于物理数据源与应用程序之间,是对数据库中的数据复制一份临时放在内存中的容器, 其作用是为了减少应 ...

  9. Session 失效原因

    转载http://blog.csdn.net/LLorJJ999/article/details/4107464 昨天去GTSC面试,有面试官问我关于Session丢失之后怎么查的问题,说老实话,开发 ...

随机推荐

  1. Selenium 2自动化测试实战11(键盘事件)

    一.键盘事件 1.Keys()类提供了键盘上几乎所有按键的方法,如下实例: #coding:utf-8 from selenium.webdriver.common.keys import Keys ...

  2. 阶段3 2.Spring_05.基于XML的IOC的案例1_3 测试基于XML的IOC案例

    编写测试方法. TestMehtod 生成测试方法 只需要改个名字叫做testFindAll 然后就复制这个方法,多复制几次改改名字 findAll方法 编写查询所有的代码 选中和这个方法,run 根 ...

  3. Session对象的生命周期(面试题/笔试题)

    创建:第一次执行request.getSession()时创建 销毁: 1)服务器(非正常)关闭时 2)session过期/失效(默认30分钟) 问题:时间的起算点 从何时开始计算30分钟? 从不操作 ...

  4. Linux man及echo的使用

    学习目标: 通过本实验掌握man和echo两个命令的用法. 实验步骤: 1.通过man查询ls的详细用法,后面可以跟哪些参数,每个参数的作用.这里主要查找如何禁止ls彩色结果输出. 2.把查找到的参数 ...

  5. 二叉平衡树AVL的插入与删除(java实现)

    二叉平衡树 全图基础解释参考链接:http://btechsmartclass.com/data_structures/avl-trees.html 二叉平衡树:https://www.cnblogs ...

  6. Java基础/网络经验

    一.Java新特性好文--掘金 1.Java8 新特性指导手册 2.Java 11 已发布,String 还能这样玩 二.Java避坑 1.为什么阿里巴巴不建议在for循环中使用"+&quo ...

  7. show slave status参数详解

    root@localhost (none)>show slave status\G *************************** 1. row ******************** ...

  8. Spring(八)-- 代理设计模式

    代理设计模式 1:基本概念 2:JDK动态代理 1. 创建接口 2. 创建实现类 3. 创建代理类 /** * jdk动态代理 不能满足 继承父类的情况 * * AnimalProxy 代理类 */ ...

  9. mac 简洁安装Kafka

    Mac 简洁安装Kafka 1.采用homebrew的方式 终端执行命令:brew install kafka 2.修改Kafka服务配置文件server.properties 执行命令: vi /u ...

  10. keepalived 容器在宿主机重启后无法启动问题:报错:daemon is already running

    初步猜测原因是:keepalived容器内的keepalived.pid文件在keepalived容器非正常退出时,没有正确删除,造成第二次启动时容器检查到pid文件已经存在,认为该进程已经存在,因为 ...