前言
🍊缘由
Java事务套着锁,就像女色缠着我
🏀事情起因:
大家好,我是JavaDog程序狗
在一个阳光明媚的中午,我的师傅突然找到我,问了我如下一个问题:
Java中已经加了锁,为什么结果却还是超卖了!
先解释下上述的几个关键词
- 我的师傅:前阿里大牛,目前屈居青岛大厂管理岗…此处省略一万字夸赞
- 加锁:模拟使用ReentrantLock加锁,多线程下建议使用Redisson分布式锁实现
- 超卖:是指系统允许多个用户购买或预订超过实际可用数量的资源
各位小伙伴先有个印象,后续本狗会详细讲解关键词
🎯主要目标
实现4大重点
1. 什么是超卖
2. 超卖如何解决
3. 事务套锁失效问题
4. 解决锁失效问题
🎁如何获取源码
本狗将测试的所有代码均已上传,包含多个示例,小伙伴们可亲测
公众号:【JavaDog程序狗】
关注公众号,发送 “lock”,无任何套路即可获得!
正文
🍅情景前置
空调租赁充值时长超卖问题
因我师傅遇到的问题代码涉及隐私,我们就模拟一个场景来分析我们的问题
举例🌰
炎炎夏日,狗哥宿舍因忍受不了酷热,租赁了一台空调,大家在清爽空调的吹拂下渐渐迷失自我
随着租赁时长到期,空调暂停工作,需要我们充值空调使用时长
我们宿舍100个赤膊大汉,分分掏出自己手机同时进行空调使用时长充值…并发超卖问题由此而来
为此设计了一个设备表,用于下方演示调试
CREATE TABLE `device` (
`id` bigint(20) NOT NULL COMMENT '主键',
`use_times` int(10) DEFAULT '0' COMMENT '使用时长',
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
🥦目标分析
一. 什么是超卖?
系统允许多个用户购买或预订超过实际可用数量的资源
👽人话解释
某件商品库存数量1件,结果卖给2个人;更形象的就是双胞胎,一个爱情结晶却喜提两个宝贝
结合上述情景前置,就是宿舍这100个人同时并发操作,按照正常逻辑每个人充值都会在基数+1小时,则总使用时长应为100。
但因为多线程并发问题,可能会导致A和B同时处理逻辑时,获取基数都是同一个,+1后同时更新入库,这样最终的总使用时长就会小于100,出现超卖问题。
出现超卖的代码
@GetMapping("/A")
@Operation(summary = "方式A-更新设备-普通方法(会出现超卖)")
public void payA(@RequestParam Long deviceId) throws InterruptedException {
for(int i=0; i<100; i++){
// 暂停20毫秒,模拟不同时间,不同人请求并发
Thread.sleep(20);
// 模拟是个100线程
new Thread(() -> {
// 更新设备-普通方法
deviceService.updateDeviceNormal(deviceId);
}).start();
}
}
@Override
public void updateDeviceNormal(Long deviceId) {