乐观锁和悲观锁在springboot下的demo

何为乐观锁?

简而言之、言而简之,见名知意,就是锁很乐观呀,就是数据变动不会太频繁,他们们只是想看看,只是在外面蹭蹭,又没有别的心思在里面,不会对数据动手动脚的,真的要是动手动脚那可就要上锁排队来哈。简单来讲:适用于读多写少,因为只有写的时候才上锁;

何为悲观锁?

悲观锁则认为:每个人都有可能对我动手动脚,所以我在大门放一把锁,谁要进来,就得先拿到钥匙。简单来讲:就是任何事务都有可能引起数据的边动,所以事务执行之前得先拿到悲观锁才可以执行;适用于写多读少的场景;因为每次读取都去校验锁,会增加系统的开销;

撸个减库存的demo

mapper.xml

    <select id="getGoodInById" parameterType="java.lang.Integer">
        select
            <include refid="Base_Column_List"/>
        from
            t_goods_in
        WHERE
            id = #{id}
    </select>

serviceImpl.java

    @Transactional(rollbackFor = Exception.class)
    public void mult(int id) {

        GoodsIn goodsIn = goodsInMapper.getGoodInById(id);

        goodsIn.setTotalNumber(goodsIn.getTotalNumber() - 1);

        goodsInMapper.updateGoodIn(JSON.parseObject(JSON.toJSONString(goodsIn), Map.class));

    }

controller.java

    @ApiOperation("更新库存")
    @GetMapping("/mult/{id}")
    public GenericResponse<Void> mult(@PathVariable(name = "id") @NotNull(message = "id不能为空") int id) throws Exception {
        return ControllerTemplate.call((GenericResponse<Void> response) -> {
            goodsInService.mult(id);
            response.setResult(true);
        });
    }

t_goods_in
在这里插入图片描述

正常请求

接下来我用postman来模拟正常的请求,以ID=45为例;
在这里插入图片描述
当70个请求完成之后,我们再去看看ID= 5的库存,此时已经由70变成0了。因为postman是串行的,当前一个请求完成之后才会发第二个请求,所以这里的结果是我预期的;

非正常请求

我用Jmeter 模拟70个线程,同步去修改库存;
在这里插入图片描述
执行完成之后我们发现,剩余库存本应该是0的,但是却显示52,这明显不符合预期嘛,那么我们来分析一下为什么会造成这种现象?
在这里插入图片描述
这是因为我启动的70个线程同时进入了mult方法根据ID取获取剩余库存,然后-1更新进去,每个线程都是独立的,大家获取到的剩余库存并不是最新的。所以会出现这种结果;

如何解决

悲观锁解决办法:在sql后面加上 for update;这也就是我前面说的,悲观锁认为每个请求都可能会去改变数据,所以每个请求进来我先把它锁住,待线程结束后才放开;大家排队一个一个来,这样就不会出错;

 <select id="getGoodInById" parameterType="java.lang.Integer" resultMap="BaseResultMap">
        select
            <include refid="Base_Column_List"/>
        from
            t_goods_in
        WHERE
            id = #{id} for  update

    </select>

乐观锁的解决办法:我把悲观锁中的for update 删除掉,在GoodsIn的entity类中添加version

 /**
     * 版本号
     */
    @Version
    private int version;

事务每次提交之后,我都把version+1的,所以当我们更新时,发现version不是我们上次查的那个,就不会更新;

  <update id="updateGoodIn" parameterType="Map">
        update t_goods_in
        set
            goodsType = #{goodsType},
            goodsName = #{goodsName},
            price = #{price},
            totalNumber = #{totalNumber},
            operator = #{operator},
            inTime = now(),
            version = version+1
        where
            id = #{id} and  version = #{version}
    </update>

所以我用Jmeter发70个并发请求,最后的库存并不是0;因为version被其他的线程更改掉了以后就更新失败了(这里不是发生异常,而是返回0条数据已被更新),所以这里我们要手动去try catch这个异常,进行重试进制;重试机制我在下一篇写;

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值