减少库存
不做处理的代码
编写最普通的程序,不考虑任何并发的代码
需求
假定现在有
10000
库存,访问一次接口库存减1
当库存等于
0
,返回库存不足
需求分析
因为需要模拟接口访问,所以需要一个web项目,我这里使用的是一个可以运行起来的
springboot
项目之前有讲过如何
快速创建springboot
项目,这里不重复赘述了。快速通道只有让接口被调用
10000
次或者更多,才能获得较准确的结果ok
,多余的没什么了,先编写代码试试
代码
新建controller
->StringRedisTestController
建立一个
static
变量->nowCount
,用来做全局值统一访问一次,让
nowCount
减一,直到小于等于0
为止
编写完成的代码如下:
package com.yxj.spring.controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
/**
* @version v1.0
* @Copyright(c): 2020-2020
* @FileName: RedisTestController.java
* @Description:
* @autho Admin
* @date 2020/1/17 13:55
*/
@RestController
@RequestMapping("/test")
public class StringRedisTestController {
private static Integer nowCount = 10000;
/**
* 减少,库存等等
* @return
*/
@GetMapping("/redis/reduce")
public String lockTest(){
if(nowCount<=0){
System.out.println("------库存不足------");
return "------库存不足------";
}
--nowCount;
System.out.println("----库存剩余----"+nowCount);
return String.valueOf(nowCount);
}
}
测试
这里为了测试方便,特意下载了
jmeter
,我的版本号apache-jmeter-2.12
启动一个线程,访问
10000
次
编写
http
请求,这里写controller
的请求路径
点击启动按钮(第一个绿色启动按钮就可以)
测试结果(单线程)
因为代码较多,这里我只贴出最终结果
第一次测试
----库存剩余----4
----库存剩余----3
----库存剩余----2
----库存剩余----1
----库存剩余----0
结果正常
第二次测试(需要重启项目,重新给static
变量赋值)
----库存剩余----4
----库存剩余----3
----库存剩余----2
----库存剩余----1
----库存剩余----0
结果正常
第三次测试(最后一次)
----库存剩余----4
----库存剩余----3
----库存剩余----2
----库存剩余----1
----库存剩余----0
结果正常
综上所诉,单线程访问代码,是没有什么问题
启动多个线程,进行访问
打开
jemeter
设置的线程组,将线程数更改为两个或者更多修改循环次数,这里的循环次数指的是每个线程的循环次数,两个线程或者更多线程去访问,让他们的乘积等于
10000
这里我设置
2
个线程,循环次数5000
测试结果(多线程)
测试(别忘记重启项目,重新获取static
的值)
----库存剩余----2699
----库存剩余----2698
----库存剩余----2696
----库存剩余----2696
可以看到,数据已经错乱了,还有重复值
增加synchronized的代码
使用JAVA原生锁
修改代码,增加synchronized
package com.yxj.spring.controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.util.Date;
/**
* @version v1.0
* @Copyright(c): 2020-2020
* @FileName: RedisTestController.java
* @Description:
* @autho Admin
* @date 2020/1/17 13:55
*/
@RestController
@RequestMapping("/test")
public class StringRedisTestController {
private static Integer nowCount = 10000;
/**
* 减少,库存等等
* @return
*/
@GetMapping("/redis/reduce")
public synchronized String lockTest(){
if(nowCount<=0){
System.out.println("------库存不足------");
return "------库存不足------";
}
--nowCount;
System.out.println("----库存剩余----"+nowCount);
return String.valueOf(nowCount);
}
}
测试结果
----库存剩余----3
----库存剩余----2
----库存剩余----1
----库存剩余----0
可以看到,结果又正常了
使用redis锁的代码
synchronized
确实可以解决库存不一致问题,但是,因为线上服务大部分都是多节点部署,两台或者两天以上的服务器,代码加synchronized
肯定是不好使的
所以这里推荐使用redis
锁
redis锁的优势
redis
锁基于redis
实现数据存储在内存,操作较快
redis
是单线程,安全
修改后的代码
使用
setIfAbsent
来判断key
是否存在使用
expire
来设置超时时间使用
delete
来删除key
package com.yxj.spring.controller;
import com.yxj.spring.utils.RedisUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.util.Date;
import java.util.concurrent.TimeUnit;
/**
* @version v1.0
* @Copyright(c): 2020-2020
* @FileName: RedisTestController.java
* @Description:
* @autho Admin
* @date 2020/1/17 13:55
*/
@RestController
@RequestMapping("/test")
public class StringRedisTestController {
@Autowired
private RedisUtils redisUtils;
private static Integer nowCount = 10000;
/**
* 减少,库存等等
* @return
*/
@GetMapping("/redis/reduce")
public String lockTest(){
if(nowCount<=0){
System.out.println("------库存不足------");
return "------库存不足------";
}
if(redisUtils.setIfAbsent(String.valueOf(nowCount), "true")){
redisUtils.expire(String.valueOf(nowCount),1,TimeUnit.SECONDS);
Integer count = nowCount;
--nowCount;
redisUtils.delete(String.valueOf(count));
System.out.println("----库存剩余----"+nowCount);
return String.valueOf(nowCount);
}else{
System.out.println("系统繁忙");
return "系统繁忙";
}
}
}
测试结果
两个线程再跑,第一个线程redis key
还没有删除,第二个线程已经进入方法了,所以被拦截,无法执行
----库存剩余----4525
系统繁忙
----库存剩余----4524
系统繁忙
----库存剩余----4523
系统繁忙
----库存剩余----4522
系统繁忙
----库存剩余----4521
系统繁忙
----库存剩余----4520
系统繁忙
----库存剩余----4519
系统繁忙
----库存剩余----4518
系统繁忙
----库存剩余----4517
系统繁忙
----库存剩余----4516
系统繁忙
----库存剩余----4515
----库存剩余----4514
----库存剩余----4513
系统繁忙
----库存剩余----4512
系统繁忙
----库存剩余----4511
系统繁忙
----库存剩余----4510
----库存剩余----4509
----库存剩余----4508
----库存剩余----4507
----库存剩余----4506
总结
快过年了,提前祝大家新年快乐
因为快过年的原因,大脑无法正常运转,导致本文有点别扭(不是有点,我感觉别扭的很
o(╥﹏╥)o
)redis
锁主要用作分布式的安全方面,可以通过最后的redis
锁测试的结果看出,虽然库存没有减到0
,但是安全得到了保障,每个数,只被用了一次我这是第一次使用
StringRedisTemplate
(之前都是使用Jedis
),没有踩过太大的坑,所以不敢保障这个redis
使用一定正确,但是效果达到了本文使用的
redisUtils
,点击下面的阅读原文,即可找着
推荐阅读
扫描二维码
加我好友
个人博客地址:
https://www.yangxj.top/