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

栏目: Java · 发布时间: 6年前

内容简介:关于synchronized锁在Spring事务中进行数据更新同步,仍出现线程安全问题

最近有小伙伴在做商品抽奖活动时,在对奖品库存进行扣减,有线程安全的问题,遂加锁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同步执行没问题。

  2. 确定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);
}

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

The Everything Store

The Everything Store

Brad Stone / Little, Brown and Company / 2013-10-22 / USD 28.00

The definitive story of Amazon.com, one of the most successful companies in the world, and of its driven, brilliant founder, Jeff Bezos. Amazon.com started off delivering books through the mail. Bu......一起来看看 《The Everything Store》 这本书的介绍吧!

UNIX 时间戳转换
UNIX 时间戳转换

UNIX 时间戳转换

RGB HSV 转换
RGB HSV 转换

RGB HSV 互转工具

HEX HSV 转换工具
HEX HSV 转换工具

HEX HSV 互换工具