Redis|在Spring事务管理下,Synchronized为啥还线程不安全?
推荐阅读:
- 秋招面试总结:Java+并发+Spring+MySQL+分布式+Redis+算法+JVM等
- 手撕分布式技术:限流、通讯、缓存 , 全部一锅端走送给你
- Spring全家桶笔记:Spring+Spring Boot+Spring Cloud+Spring MVC
文章插图
在synchronized 锁住方法的情况下 , 竟然出现了脏写Tips昨天本来打算是准备着一支烟 一杯咖啡 一个bug写一天的,突然我们组长跟我们说线上环境报错了,还出现了"服务器异常 , 请联系管理员"这特么不是一级事故吗?虽然有测试再前面扛枪 。 但是是我负责的直播模块 , 心理慌的一批(ps 报错图当时没保存了)
分析事故原因因为是报错(因为我做这条数据查询的时候是selectOne 所以会报出现了sql异常) 原因到是很快找到了 数据库出现了脏写如图:
文章插图
我负责的是直播模块 其中的一个业务是直播结束后第三方会通知我去拉取直播的回放 , 但是这个回放有可能一条 , 也有可能是多条 , 但是我们的业务要求是只需要保存一条直播回放所以我这会做如下操作:
文章插图
我再做插入之前我会做一个校验 , 并且我还加了一个方法级别的锁 并且线上我们只有一个副本 , 竟然还出现了脏写 我的fuck,我这是见了鬼了吧
解决问题的过程我怀着百私不得其解的心理打算去找答案
首先我模拟了一个并发环境:
@Test public void TEST_TX() throws Exception { int N = 2; CountDownLatch latch = new CountDownLatch(N); for (int i = 0; i < N; i++) { Thread.sleep(100L); new Thread(() -> { try { latch.await(); System.out.println("---> start " + Thread.currentThread().getName()); Thread.sleep(1000L); CourseChapterLiveRecord courseChapterLiveRecord = new CourseChapterLiveRecord(); courseChapterLiveRecord.setCourseChapterId(9785454l); courseChapterLiveRecord.setCreateTime(new Date()); courseChapterLiveRecord.setRecordEndTime(new Date()); courseChapterLiveRecord.setDuration("aaa"); courseChapterLiveRecord.setSiteDomain("ada"); courseChapterLiveRecord.setRecordId("aaaaaaaaa"); courseChapterLiveRecordServiceImpl.saveCourseChapterLiveRecord(courseChapterLiveRecord); System.out.println("---> end " + Thread.currentThread().getName()); } catch (Exception e) { e.printStackTrace(); } }).start(); latch.countDown(); } }通过CountDownLatch 去模拟并发看看数据是否会有问题:结果测试线的数据如下:
文章插图
我去还真出现了 而且是一部分出现脏写 , 一部分没有成功 , 我特么 fuck 心理一万次想说这特么我怎么找测了十来次 然后觉得肯定是有问题的 然后冷静下来 因为我打了日志 发现2个线程确实是顺序执行的(这里的截图就没有贴了)众所周知 , synchronized方法能够保证所修饰的代码块、方法保证有序性、原子性、可见性 。那么这说明什么呢 我一想肯定Synchronized 它是起到它的作用的 一个线程执行完成之后 , 另外一个线程再来执行 ,突然灵光一闪 是不是下一个线程再做幂等校验的时候 读到了上一次还没有提交的事务 所以造成了脏读 , 脏写的原因呢 然后我把再类上的 @Transactional 注解去掉
文章插图
果然后面测了几次 再也没出现上面的情况了
Tips 特别感谢一位不愿透露姓名的大佬的指出说我没有把标题的内容说清楚和后面的解决问题的收场的时候有点草率
在这里 我再好好的说一下我标题是 在Spring事务管理下 , Synchronized为啥还线程不安全? 其实有是自己并没有用Synchronized 锁住 Spring 的事务因为我的列子上的@Transaction注解是再类上面(也就是再方法上面)Spring的声明事事务他是利用了aop的思想我虽然锁住了第一个线程 但是等到第一个线程的事务 还没提交的时候 , 第二个线程就去查询了 所以就会导致线程不安全问题
解决问题方案1 很简单 那就是不开事务就行了 , 再这个方法上不加事务就行 因为 Synchronized 可以保证线程安全 。这个方案的意思就是说不要再同一个方法上用@Transaction 和 Synchronized 例子图就没有贴了 就像我前面的 把注解去掉就好了 (但是前提你这个方案确定是不需要事务)
方案2 再这个里面再调用一层service 让那个方法提交事务 , 这样的话加上Synchronized 也能保证线程安全 。方案2我贴下代码吧
@Override public synchronized void saveCourseChapterLiveRecord(CourseChapterLiveRecord courseChapterLiveRecord) { saveRecord(courseChapterLiveRecord); } @Transactional public void saveRecord(CourseChapterLiveRecord courseChapterLiveRecord) { //先查数据看是否已经存了 if (findOrder(courseChapterLiveRecord)){ return;} int row = this.insertSelective(courseChapterLiveRecord); if (row
- 麒麟|荣耀新款,麒麟810+4800万超清像素,你还在犹豫什么呢?
- 智能手机市场|华为再拿第一!27%的份额领跑全行业,苹果8%排在第四名!
- 行业|现在行业内客服托管费用是怎么算的
- 零部件|马瑞利发力电动产品,全球第七大零部件供应商在转型
- 通气会|12月4~6日,2020中国信息通信大会将在成都举行
- 俄罗斯手机市场|被三星、小米击败,华为手机在俄罗斯排名跌至第三!
- 体验|闭上眼睛点外卖是什么感觉?时隔一年再次体验,进步令人欣慰
- 当初|这是我的第一部华为手机,当初花6799元买的,现在“一文不值”?
- 出海|出海日报丨短视频生产服务商小影科技完成近4亿元 C 轮融资;华为成为俄罗斯在线出售智能手机的第一品牌
- 看过明年的iPhone之后,现在下手的都哭了