准备环境:
1、jdk
2、mysql
3、redis
4、idea
5、postman
项目源码:
链接:https://pan.baidu.com/s/17epRS84-MxXYnj8wESyypQ
提取码:m8ie
实现的原理主要是在redis中记录某个Key来记录该业务是否正在被其他线程所占用
- 工程目录
这里主要贴一下RedisLock和GoodsService
package com.test.service;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.test.config.RedisLock;
import com.test.entity.Goods;
import com.test.mapper.GoodsMapper;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.io.Serializable;
@Service
public class GoodsService extends ServiceImpl<GoodsMapper, Goods> {
private final static Logger logger = LoggerFactory.getLogger(GoodsService.class);
@Autowired
private RedisLock redisLock;
/**
* 超时时间 5s
*/
private static final int TIMEOUT = 5 * 1000;
public String updateNumById(Integer id, String code, Integer count) {
// 上锁
long time = System.currentTimeMillis() + TIMEOUT;
if (!redisLock.lock(code, String.valueOf(time))) {
return "排队人数太多,请稍后再试.";
}
try {
Goods goods = getById(id);
if (goods != null) {
if (goods.getNum() <= 0) {
return "商品卖完了";
}
if (goods.getNum() < count) {
return "商品卖完了,剩余数量:" + goods.getNum() + " 你购买数量:" + count;
}
System.out.println("剩下多少:" + goods.getNum());
System.out.println("减少多少:" + count);
goods.setNum(goods.getNum() - count);
baseMapper.updateById(goods);
return "购买成功";
} else {
return "商品不存在";
}
} finally {
// 解锁
redisLock.release(code, String.valueOf(time));
}
}
@Override
public Goods getById(Serializable id) {
return baseMapper.selectById(id);
}
}
package com.test.config;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
@Component
public class RedisLock {
@Autowired
private StringRedisTemplate stringRedisTemplate;
/**
* 加锁
*
* @param code 加锁的Key
* @param timeStamp 时间戳:当前时间+超时时间
* @return
*/
public boolean lock(String code, String timeStamp) {
if (stringRedisTemplate.opsForValue().setIfAbsent(code, timeStamp)) {
// 如果key不存在,上锁成功
return true;
}
// 上锁失败
// 判断是否超时
String lock = stringRedisTemplate.opsForValue().get(code);
if (!StringUtils.isEmpty(lock) && Long.parseLong(lock) < System.currentTimeMillis()) {
// 锁已经存在,这里代表已经存在的锁
String preLock = stringRedisTemplate.opsForValue().getAndSet(code, timeStamp);
// 这个操作为了获取锁的同时,更新时间戳,万一同时有两个线程来到这里,确保timeStamp是唯一的
if (!StringUtils.isEmpty(preLock) && preLock.equals(lock)) {
return true;
}
}
return false;
}
/**
* 释放锁
*
* @param code
* @param timeStamp
*/
public void release(String code, String timeStamp) {
try {
String currentValue = stringRedisTemplate.opsForValue().get(code);
if (!StringUtils.isEmpty(currentValue) && currentValue.equals(timeStamp)) {
stringRedisTemplate.opsForValue().getOperations().delete(code);
}
} catch (Exception e) {
System.out.println("解锁异常");
}
}
}