工作当中经常会遇到既需要开启事务管理,同时也需要同步保证线程安全的场景。

比如一个方法

@Transactional
public synchronized void test(){
//
}

不知道大家有没有这样写过?

这样写会有问题吗?

众所周知,spring使用动态代理加AOP实现事务管理。那么上面的方法实际上需要简化成3个步骤:

void begin();

@Transactional
public synchronized void test(){
//
} void commit();
// void rollback();

先看事务本身,这里简化为test()一个方法,从而忽略事务的传播来看,它是不受synchronized 影响的。因为它能正常commitrollback不受其它线程影响。

再看同步这一块,这里很明显就有点问题了。synchronized 至少无法作用于事务开启提交这一步骤。假设一个线程先在此方法做了update,在return之后commit之前,另一个线程进入了此方法,进行了select,

除非事务隔离级别为读未提交(READ_UNCOMMITTED)(话说回来谁会在生产环境用这种隔离级别呢),不然第二个线程读到的是未修改的值。

而这肯定并未与使用synchronized 的初衷相符。

口说无凭,show you me code。

@Transactional
public synchronized void test(){
log.info("表哥,我进来了哦!");
// select
Person person = personDao.selectByPrimaryKey("1");
log.info("person.name = " + person.getName());
// update
person.setName("你是谁?你根本不是我表哥!");
int result = personDao.updateByPrimaryKeySelective(person);
log.info("result = " + result);
}

然后在TransactionAspectSupport.commitTransactionAfterReturning()加个断点。

再模拟两个请求执行,在断点处观察第2次请求与第1次请求select到的值是不是一致。一致说明synchronized 在这里没有达到预期目的。

注意断点时选thread不要选all,不然第2个线程进不来。

通过执行结果,可以看到第2个线程执行的时候访问到了还是原值张三,并在update时等待锁超时。

通过什么方法可以让synchronized 达到预期效果呢?

手动开启事务?pass。

事务传播级别?无关。

事务隔离级别?

读未提交 不实用。还是试试效果。

加上隔离级别

看看效果

能满足要求,但是谁会在生产环境使用读未提交级别?

依次往上,

读已提交,经测试不满足,

可重复读,mysql默认级别,一开始就是,不满足。

SERIALIZABLE

这倒是走向了另一个极端,通过测试可以看到,由于在SERIALIZABLE隔离级别下,会给表加个锁,因此在第2个线程执行到Select的时候就会一直等待到锁超时。

在这一个固定的测试场景曲线满足了业务要求,但是它还是进入了test方法,因此不满足synchronized的要求。

而且这种隔离级别和读未提交一样属于两个极端,它会极大的抑制并发数,在生产环境中也极少使用,在这里属于既不实用也不好用。

for update

给select查询语句手动上锁。

测试结果就不截图了,这种是比SERIALIZABLE要实用一些,它只加行锁,其它的话类似,并不能完全达到synchronized的要求。

是否使用看场景。

给test方法加一层调用方法

transcationalsynchronized分开,作用在两个方法。比如synchronized在上层方法。

千万别写成这样,这样事务不生效了。

最好也别写到最顶层如controller层,这样感觉把通道门口就给堵死了的感觉。只是加个中间层。

这样,表哥有了第1次的经验过后,表妹在第2次来的时候就被小区安保直接给拦住啦,在表哥完全跑路之前没有撬锁的机会了。

另外,一定是synchronized在调用层,transcational在被调用层。不能弄反了,弄反了就和之前没区别了。

总结

当transcational遇上synchronized,不要搞在一起,会出事。

如果要用最好是分开。

当transcational遇上synchronized的更多相关文章

  1. 谁还没遇上过NoClassDefFoundError咋地——浅谈字节码生成与热部署

    谁还没遇上过NoClassDefFoundError咋地--浅谈字节码生成与热部署 前言 在Java程序员的世界里,NoClassDefFoundError是一类相当令人厌恶的错误,因为这类错误通常非 ...

  2. MVC遇上bootstrap后的ajax表单模型验证

    MVC遇上bootstrap后的ajax表单验证 使用bootstrap后他由他自带的样式has-error,想要使用它就会比较麻烦,往常使用jqueyr.validate的话只有使用他自己的样式了, ...

  3. 敏捷遇上UML-需求分析及软件设计最佳实践(郑州站 2014-6-7)

      邀请函: 尊敬的阁下:我们将在郑州为您奉献高端知识大餐,当敏捷遇上UML,会发生怎样的化学作用呢?首席专家张老师将会为您分享需求分析及软件设计方面的最佳实践,帮助您掌握敏捷.UML及两者相结合的实 ...

  4. 敏捷遇上UML—软创基地马年大会(广州站 2014-4-19)

        我们将在广州为您奉献高端知识大餐,当敏捷遇上UML,会发生怎样的化学作用呢?首席专家张老师将会为您分享需求分析及软件设计方面的最佳实践,帮助您掌握敏捷.UML及两者相结合的实战技巧. 时间:2 ...

  5. 敏捷遇上UML——软创基地马年大会(深圳站 2014-3-15)

    邀请函: 尊敬的阁下: 我们将在深圳为您奉献高端知识大餐,当敏捷遇上UML,会发生怎样的化学作用呢?首席专家张老师将会为您分享需求分析及软件设计方面的最佳实践,帮助您掌握敏捷.UML及两者相结合的实战 ...

  6. 初识genymotion安装遇上的VirtualBox问题

    想必做过Android开发的都讨厌那慢如蜗牛的 eclipse原生Android模拟器吧! 光是启动这个模拟器都得花上两三分钟,慢慢的用起来手机来调试,但那毕竟不是长久之计,也确实不方便,后来知道了g ...

  7. SQL SERVER 2008 R2 SP1更新时,遇上共享功能更新失败解决方案

    SQL SERVER 2008 R2 SP1更新时,遇上共享功能更新失败的问题,可作如下尝试: 更新失败后,在windows的[事件查看器→应用程序]中找到来源为MsiInstaller,事件ID为1 ...

  8. 当创业遇上O2O,新一批死亡名单,看完震惊了!

    当创业遇上O2O,故事就开始了,总投入1.6亿.半年开7家便利店.会员猛增至10万……2015半年过去后,很多故事在后面变成了一场创业“事故”,是模式错误还是烧钱过度?这些项目的失败能给国内创业者带来 ...

  9. LoadRunner - 当DiscuzNT遇上了Loadrunner(下) (转发)

    当DiscuzNT遇上了Loadrunner(下) 在之前的两篇文章中,基本上介绍了如何录制脚本和生成并发用户,同时还对测试报告中的几个图表做了简单的说明.今天这篇文章做为这个系列的最后一篇,将会介绍 ...

  10. LoadRunner - 当DiscuzNT遇上了Loadrunner(中) (转发)

    当DiscuzNT遇上了Loadrunner(中) 在上文中,介绍了如果录制脚本和设置脚本执行次数.如果经过调试脚本能够正常工作的话,就可以设置并发用户数并进行压力测试了. 首先我们通过脚本编辑界面上 ...

随机推荐

  1. vantUI <van-uploader> 上传图片,如何获取图片的尺寸

    html代码 <van-uploader preview-size="300px" style="width:300px;display:block;margin: ...

  2. 4、excel快速排序从1开始

    在分世界杯的文件时,要求把每一行从1开始排列,自己的做法就是先输入1和2,然后拖黑1和2,接着鼠标一直往下拖,这样就了. 公式的方法: 输入公式=Row()-1,如何在这个单元格的右下角双击就可以了 ...

  3. 初步理解1=C

    1=C 1=C 表示简谱上看到1就弹C键 1=? 1就从?键出发 例如我们看到1=D, 我们看到1就从D键出发 1=C 到1=D就叫做升了一个key 黑键怎么表示 半音与全音 半音是一个距离单位 1 ...

  4. 解决element-ui表格字段用expand展开行时其他字段也会同时展开的问题

    使用element-ui的table表格时,我们通常会在需要展开的列上通过设置 type="expand" 和 Scoped slot 可以开启展开行功能,但是渲染结果会出现其他未 ...

  5. HashMap长度为什么是2的幂

    虽然hash值很多,范围很大,但是内存存不了那么大的数组,所以取hash的散列值的时候,需要用hash值,除以数组长度取余数.又由于取余数(%)的性能不如与运算(&),所以想用与运算来代替取余 ...

  6. [jmeter的使用]jmeter上传文件接口的写法

    1.类型和参数名称,依据接口/抓的包决定 2.必须选择java否则报错 3.http header也要写

  7. PVE设置定时关闭、启动虚拟机

    shell中输入命令: crontab -e 进入对应cron 添加命令: 例如: 00 2 * * * pvesh create /nodes/pve/qemu/102/status/stop 00 ...

  8. 20181224蒋嘉豪-exp3-免杀原理与实现

    20181224蒋嘉豪-exp3-免杀原理与实现 目录 20181224蒋嘉豪-exp3-免杀原理与实现 课上知识点总结 1.恶意软件检测机制 2.免杀技术综述 Exp3.1 能够正确使用msf编码器 ...

  9. python中的变量定义

    1 变量名:由下划线.字母和数字组成 2 python中变量名的特殊含义: xx:标准的标识符,共有的 _xx:只是在from - import *时是不会被导入的,其他导入方式会被引入 隐藏变量还有 ...

  10. flink常用命令

    1.查看flink任务 /var/lib/hadoop-hdfs/flink/bin/flink  list 2.停止flink任务 /var/lib/hadoop-hdfs/flink/bin/fl ...