为什么80%的码农都做不了架构师?>>>   

#1 问题描述# 最近有小伙伴在做商品抽奖活动时,在对奖品库存进行扣减,有线程安全的问题,遂加锁synchronized进行同步,但发现加锁后并没有控制住库存线程安全的问题,导致库存仍被超发。

先简单介绍下,各层的技术架构:

中间层框架:Spring 4.1.0

持久层:MyBatis 3.2.6

MVC框架:Spring MVC 4.1.0

存在问题的代码:

@Override
public void saveMemberTicket(ApplyTicketReq applyTicketReq) throws ServiceException {synchronized (this.class) {// 检查库存是否有剩余preCheck(applyTicketReq);// 扣减库存modifyTicketAmount(applyTicketReq);}
}

库存扣减超发问题具体描述:

  1. 当库存剩余为1时,线程1拿到锁进入同步代码块,扣减库存,线程2等待锁;

  2. 当线程1执行完同步代码块时,线程2拿到锁,执行同步代码块,检查到的库存剩余仍为1;【此时,库存应该为0,产生库存扣减超发问题】

#2 排查问题# 排查问题开始之前,简单说下自己排查问题的几个原则(仅供参考):

  1. 问题重现:一定要先重现问题,任何重现不了的问题,都不是问题。同理,任何存在的问题,都必然能再次重现。

  2. 由近及远:先确认自己的代码无问题,然后再去确认外部代码无问题(如:框架代码,第三方代码等)。

  3. 由外到内:程序就是一个IPO,有输入Input(如:参数、环境等)也有输出Out(如:结果、异常等),输出Out是问题的表象,先确定外部因素Input无问题,再确认程序代码逻辑无问题。

  4. 由浅入深:其实就是由易到难、自上向下,先从上层应用排查问题,如:上层API、应用层、HTTP传输等,然后再确认底层应用排查问题,如:底层API、网络层、系统层、字节码、JVM等;

  1. 确定synchronized关键字是否起作用;【建议:尽量慎用synchronized关键字,非常影响程序性能】

根据多线程并发测试,可以确认多线程之间是同步执行synchronized代码块,确认synchronized同步执行没问题。

  1. 确定Spring事务是否提交成功;查看Spring 事务配置:
<!-- Transaction Support -->
<tx:advice id="useTxAdvice" transaction-manager="txManager"><tx:attributes><tx:method name="*remove*" propagation="REQUIRED" read-only="false" rollback-for="java.lang.Exception" no-rollback-for="com.xxx.exception.ServiceException"/><tx:method name="*save*" propagation="REQUIRED" read-only="false" rollback-for="java.lang.Exception" no-rollback-for="com.xxx.exception.ServiceException"/><tx:method name="*modify*" propagation="REQUIRED" read-only="false" rollback-for="java.lang.Exception" no-rollback-for="com.xxx.exception.ServiceException"/><tx:method name="*update*" propagation="REQUIRED" read-only="false" rollback-for="java.lang.Exception" no-rollback-for="com.xxx.exception.ServiceException"/><tx:method name="create*" propagation="REQUIRED" read-only="false" rollback-for="java.lang.Exception" no-rollback-for="com.xxx.exception.ServiceException"/><tx:method name="fill*" propagation="REQUIRED" read-only="false" rollback-for="java.lang.Exception" no-rollback-for="com.xxx.exception.ServiceException"/><tx:method name="cancel*" propagation="REQUIRED" read-only="false" rollback-for="java.lang.Exception" no-rollback-for="com.xxx.exception.ServiceException"/><tx:method name="*chang*" propagation="REQUIRED" read-only="false" rollback-for="java.lang.Exception" no-rollback-for="com.xxx.exception.ServiceException"/><tx:method name="handleLotteryResult" propagation="REQUIRED" read-only="false" rollback-for="java.lang.Exception" no-rollback-for="com.xxx.exception.ServiceException"/><tx:method name="find*" propagation="SUPPORTS"/><tx:method name="get*" propagation="SUPPORTS"/><tx:method name="query*" propagation="SUPPORTS"/><tx:method name="page*" propagation="SUPPORTS"/><tx:method name="count*" propagation="SUPPORTS"/></tx:attributes>
</tx:advice><!--把事务控制在Service层-->
<aop:config><aop:pointcut id="pc" expression="execution(public * com.xxx..service.*.*(..))" /><aop:advisor pointcut-ref="pc" advice-ref="useTxAdvice" />
</aop:config>

由于Spring事务是通过AOP实现的,所以在saveMemberTicket方法执行之前会有开启事务,之后会有提交事务逻辑。而synchronized代码块执行是在事务之内执行的,可以推断在synchronized代码块执行完时,事务还未提交,其他线程进入synchronized代码块后,读取的库存数据不是最新的

#3 解决问题# 将synchronized关键字加入到Controller层,使synchronized锁的范围大于事务控制的范围。

@RequestMapping(value = "applyTicket")
@ResponseBody
public void applyTicket(@FromJson ApplyTicketReq applyTicketReq) throws Exception {synchronized (String.valueOf(applyTicketReq.getMemberRoomId()).intern()) {synchronized (String.valueOf(applyTicketReq.getTicketId()).intern()) {service.saveMemberTicket(applyTicketReq);}}responseMessage(ModelResult.CODE_200,ModelResult.SUCCESS);
}

#4 总结问题# 根据以上的排查过程,已经很清楚的确认了事务与锁之间存在的问题。由于事务范围大于锁代码块范围,在锁代码块执行完成后,此时事务还未提交,导致此时进入锁代码块的其他线程,读到的仍是原有的库存数据。

关于程序加锁自己的一点见解:

  1. 建议程序中尽量不要加锁;

  2. 尽量在业务和代码层,解决线程安全的问题,实现无锁的线程安全;

  3. 如果以上两点都做不到,一定要加锁,尽量使用java.util.concurrent包下的锁(因为是非阻塞锁,基于CAS算法实现,具体可以查看AQS类的实现);

  4. 如果以上三点仍然都做不到,一定要加阻塞锁:synchronized锁,两个原则:(1)尽量减小锁粒度;(2)尽量减小锁的代码范围;

转载于:https://my.oschina.net/xianggao/blog/677998

关于synchronized锁在Spring事务中进行数据更新同步,仍出现线程安全问题相关推荐

  1. java 锁表后事务提交_关于synchronized锁在Spring事务中进行数据更新同步,仍出现线程安全问题...

    #1 问题描述# 最近有小伙伴在做商品抽奖活动时,在对奖品库存进行扣减,有线程安全的问题,遂加锁synchronized进行同步,但发现加锁后并没有控制住库存线程安全的问题,导致库存仍被超发. 先简单 ...

  2. 【Spring 】Synchronized锁在Spring事务管理下,为啥还线程不安全?

    1.概述 转载:Synchronized锁在Spring事务管理下,为啥还线程不安全? 知乎问题:知乎

  3. spring事务中的超时时间很多人都不理解

    在spring中如何使用事务是一个很大的问题,其中有一个我需要在这篇文章中着重讲解一下,就是spring事务中的超时时间问题,很多同学不知道如何才能是这个超时时间生效,导致在使用过程中出现各种各样的问 ...

  4. spring框架中的单例Beans是线程安全的么?

    看到这样一个问题:spring框架中的单例Beans是线程安全的么? Spring框架并没有对单例bean进行任何多线程的封装处理.关于单例bean的线程安全和并发问题需要开发者自行去搞定.但实际上, ...

  5. Spring框架中的单例Beans是线程安全的么

    看到这样一个问题:Spring框架中的单例Beans是线程安全的么? Spring框架并没有对单例bean进行任何多线程的封装处理.关于单例bean的线程安全和并发问题需要开发者自行去搞定.但实际上, ...

  6. 同一事务多次加for_Synchronized锁在Spring事务管理下,为啥还线程不安全?

    前言 只有光头才能变强. 文本已收录至我的GitHub仓库,欢迎Star:https://github.com/ZhongFuCheng3y/3y 大年初二,朋友问了我一个技术的问题(朋友实在是好学, ...

  7. spring 事务中先删除再插入后唯一键冲突 delete then insert duplicate key

    今天在同事碰到了一个比较有意思的问题,为了实现某个场景中的数据更新和删除,想通过 delete all entities 然后 insert new entities 的方式减少判断数据是否删除的操作 ...

  8. Spring框架中的单例Bean是线程安全的么?

    答: 不是安全的. Spring中的Bean默认是单例模式的,框架并没有对bean进行多线程的封装处理. 注:单例bean是指IOC容器中就只有这么一个bean,是全局共享的,有多少个线程来访问用的都 ...

  9. Spring框架中的单例Bean是线程安全的吗?

    首先直接给出答案:不是线程安全的 一.分析问题 证明不是线程安全的案例如下: public class Student {private String stuName;public String re ...

最新文章

  1. C 语言中 void* 详解及应用介绍
  2. 先随机后排序的oracle,Oracle用decode函数或CASE-WHEN实现自定义排序
  3. 皮一皮:落伍了落伍了
  4. linux 中的if else语句
  5. java aws s3_java操作AWS S3一些坑记录
  6. ORACLE SQL*PLUS 命令大全
  7. 分拣外观残缺的机器人_一款分拣搬运机器人的设计
  8. C++中的继承(派生)的一些误区
  9. 信息系统项目管理师证书有什么用?
  10. 定制版商业计划书PPT模板
  11. spring data jpa 动态查询Specification(包括各个In、like、Between等等各种工具类,及完整(分页查询)用法步骤(到返回给前端的结果))
  12. Halo博客搭建笔记(以Nginx反向代理 + 负载均衡 + 服务的方式运行Halo博客)
  13. 大道至简(原标题:少是指数级的多)
  14. 关于Xcode修改APP名称
  15. 【BZOJ-4316】小C的独立集 仙人掌DP + 最大独立集
  16. Conflux 联合创始人、CTO伍鸣博士出席杭州钱江世纪城重点发展企业座谈会
  17. 今日头条2018 坐标
  18. 连续复利怎么用计算机算,请问银行的连续复利计算公式
  19. @keyup.enter.native不生效问题解决
  20. [Translation]《迈陂塘》

热门文章

  1. win7安装和配置IIS7
  2. 出口-汇聚-接入层组网设计
  3. Win11 22H2 22621.754(KB5018496)RP测试版推送了!
  4. 江苏省教育考试院计算机考试报名时间,江苏省2018年上半年计算机等级考试报名官网:江苏省教育考试院www.jseea.cn...
  5. 博睿数据2021战略发布巡展,开辟IT运维创新路径
  6. 数据库还原不了的问题
  7. STM32学习笔记(超详细整理145个问题)
  8. android 修改aar-实践
  9. 电压偏置测量电路的固定电阻选择
  10. pyecharts数据可视化—雷达图、折线图、面积图