记录使用Redis分布式锁、悲观锁、synchronized防止超卖问题
在网上看到使用一些锁机制保证高并发情况,商品超卖的问题,我就自己写了个demo。
前置条件:建议新建一个springboot工程(添加web依赖),然后自行整合mybatisplus、redis,可以参照以下链接:
mybatisPlus:https://blog.csdn.net/wang20000102/article/details/132615071
redis:https://blog.csdn.net/lwj_07/article/details/126265935。就只需要引入依赖和添加redis配置
1、使用redis当分布式锁:
核心代码如下:
controller层
service
serviceImpl
@Override @Transactional public void testConcurrent() { //使用redis实现分布式锁setnx String CHANGE_DEMO = "change_demo"; Boolean flag = redisTemplate.opsForValue().setIfAbsent(CHANGE_DEMO, Thread.currentThread().getId(), 5, TimeUnit.SECONDS); //如果拿到了锁,执行业务逻辑 if (flag) { try { log.info("线程{}拿到锁,开始执行业务逻辑",Thread.currentThread().getId()); Device device = deviceMapper.selectById(3); //锁过期时间是5秒 这里我让线程休眠5s 那键值肯定就是过期了 看看有什么问题 //Thread.sleep(6000); if (device.getSortNum() > 0) { log.info("当前库存是:{}", device.getSortNum()); int i = device.getSortNum() - 1; device.setSortNum(i); Integer integer = deviceMapper.updateById(device); Device select = deviceMapper.selectById(3); log.info("线程{}抢到商品,当前减后库存是:{}", Thread.currentThread().getId(), select.getSortNum()); } }catch (Exception e){ log.info("扣除库存中出现异常",e); }finally { //谁加锁 谁解锁 if (Objects.equals(redisTemplate.opsForValue().get(CHANGE_DEMO),Thread.currentThread().getId())){ redisTemplate.delete(CHANGE_DEMO); log.info("线程{}解锁成功",Thread.currentThread().getId()); } } } }
配置文件application.yaml
数据库表:
使用ApiFox 进行并发请求:
控制台打印:因为数据库是3,所以会有三次扣减库存动作
参照如下链接:https://blog.csdn.net/weixin_44130574/article/details/125678000
2、使用悲观锁防止超卖:
代码
//先查库存 Device device = deviceMapper.selectByIdUpdate(3); // 核心为该行代码 //Thread.sleep(3000); if (device.getSortNum() > 0) { System.out.println("当前库存是:" + device.getSortNum()); int i = device.getSortNum() - 1; device.setSortNum(i); Integer integer = deviceMapper.updateById(device); Device device1 = deviceMapper.selectById(3); System.out.println("抢到商品,当前减后库存是:" + device1.getSortNum()); System.out.println("成功"); } else { System.out.println("当前没抢到线程是:" + Thread.currentThread().getName()); System.out.println("没有库存了"); }
其实只是在第一行查询那添加一个for update进行行锁的控制
可参照链接:https://blog.csdn.net/cxclll/article/details/130602547
3、使用synchronized就行加锁
代码如下:
synchronized (this) { //先查库存 Device device = deviceMapper.selectById(3); //Thread.sleep(3000); if (device.getSortNum() > 0) { System.out.println("当前库存是:" + device.getSortNum()); int i = device.getSortNum() - 1; device.setSortNum(i); Integer integer = deviceMapper.updateById(device); Device device1 = deviceMapper.selectById(3); System.out.println("抢到商品,当前减后库存是:" + device1.getSortNum()); System.out.println("成功"); } else { System.out.println("当前没抢到线程是:" + Thread.currentThread().getName()); System.out.println("没有库存了"); } }
注意:使用synchronized不能跟声明式事务@Transactional注解一起使用,原因是声明式事务使用aop处理事务问题,会出现锁释放了但是事务没提交导致锁失效的问题
可参照链接:https://blog.csdn.net/cxclll/article/details/130602547
4、也可使用乐观锁控制
注:不是抄袭,我只是把代码自行实现下,觉得作者写的很好。如有侵权,联系我删除文章。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 分享4款.NET开源、免费、实用的商城系统
· 全程不用写代码,我用AI程序员写了一个飞机大战
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· 白话解读 Dapr 1.15:你的「微服务管家」又秀新绝活了
· 上周热点回顾(2.24-3.2)