先看一段代码,这段代码初步看好像没有并发问题,但是如果第一次执行不存在的redisKey时候,就有可能存在并发问题。
优化之后的代码如下:
/**
* 生成自增取餐码
*
* @param shopId 店铺id
* @return String
* @author knight
* @time 2022/3/23 17:25
*/
public String getIncreaseCode(Integer shopId) {
int code;
String accCodeKey = "increase_code:" + shopId ;
Boolean hasKey = this.redisTemplate.hasKey(accCodeKey);
if(hasKey){
code = Math.toIntExact(redisTemplate.opsForValue().increment(accCodeKey, 1));
}else {
code=1000001;
//利用redis的单线程原理,规避并发问题的产生
Boolean ifAbsent = redisTemplate.opsForValue().setIfAbsent(accCodeKey, code);
if (!ifAbsent){
code = Math.toIntExact(redisTemplate.opsForValue().increment(accCodeKey, 1));
}
}
return String.valueOf(code).substring(1);
}
测试代码如下
package com.xdd.v2.service;
import com.xdd.manage.OrderManage;
import javax.annotation.Resource;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;
/***
* SechillManage 类单元测试
*
* @since 单元测试
* @author coffee
* @version V1.0
* @code 2023/11/27
*/
@SpringBootTest
@RunWith(SpringRunner.class)
public class OrderManageManageTest {
@Resource
private OrderManage orderManage;
@Test
public void getIncreaseCodeTest(){
Integer shopId = 6;
MyRunnable myRunnable = new MyRunnable(orderManage, shopId);
new Thread(myRunnable).start();
new Thread(myRunnable).start();
new Thread(myRunnable).start();
new Thread(myRunnable).start();
new Thread(myRunnable).start();
new Thread(myRunnable).start();
new Thread(myRunnable).start();
new Thread(myRunnable).start();
new Thread(myRunnable).start();
new Thread(myRunnable).start();
}
}
class MyRunnable implements Runnable{
private OrderManage orderManage;
private Integer shopId;
public MyRunnable(OrderManage orderManage, Integer shopId) {
this.orderManage = orderManage;
this.shopId = shopId;
}
@Override
public void run() {
String increaseCode = orderManage.getIncreaseCode(shopId);
System.out.println("线程名称:"+Thread.currentThread().getName()+",increaseCode:" + increaseCode);
}
}
经过多次测试,已经不会有并发问题了。
总结:解决这个并发问题有多种思路
1,利用redis的单线程特性,可以解决并发问题(本文就是这种思路,这种思路比较巧妙)
2,使用分布式锁,将redis的读写操作包裹在一起,优点编码简单,缺点锁的读写操作会有两次网络请求,增加了接口的响应时间
3,使用redis的lua脚本方式执行读写,lua脚本会将读写操作当成一个整体来执行,优点是程序不用额外加锁,减少资源的消耗,缺点编码可能复杂一些
4,可以项目启动的就初始化这个redis,程序使用的时候就不用走if判断