關鍵代碼:
@Component
@Slf4j
public class RedisLock {
@Autowired
private StringRedisTemplate redisTemplate;
/**
* 加鎖
* @param key 商品id
* @param value 目前時間+逾時時間
* @return
*/
public boolean lock(String key, String value) {
//setIfAbsent()也就是redis的setnx,當key不存在時設定value
if(redisTemplate.opsForValue().setIfAbsent(key, value)) {
//加鎖成功
return true;
}
//當鎖已存在,可以擷取該鎖的value,來判斷是否過期
String currentValue = redisTemplate.opsForValue().get(key);
//如果鎖過期
if (!StringUtils.isEmpty(currentValue)
&& Long.parseLong(currentValue) < System.currentTimeMillis()) {
//擷取上一個鎖的時間
String oldValue = redisTemplate.opsForValue().getAndSet(key, value);
//如果多個線程同時進入這裡,則可以通過判斷oldValue與currentValue是否相等來限制多個線程加鎖
if (!StringUtils.isEmpty(oldValue) && oldValue.equals(currentValue)) {
return true;
}
}
return false;
}
/**
* 解鎖
* @param key
* @param value
*/
public void unlock(String key, String value) {
try {
String currentValue = redisTemplate.opsForValue().get(key);
if (!StringUtils.isEmpty(currentValue) && currentValue.equals(value)) {
redisTemplate.opsForValue().getOperations().delete(key);
}
}catch (Exception e) {
log.error("【redis分布式鎖】解鎖異常, {}", e);
}
}
}
public void orderProductMockDiffUser(String productId)
{
//加鎖
long time=System.currentTimeMillis()+TIMEOUT;
if(!redisLock.lock(productId,String.valueOf(time))){
throw new SellException(ResultEnum.REDIS_LOCK_FAIL);
}
//1.查詢該商品庫存,為0則活動結束。
int stockNum = stock.get(productId);
if(stockNum == 0) {
throw new SellException(100,"活動結束");
}else {
//2.下單(模拟不同使用者openid不同)
orders.put(KeyUtil.genUniqueKey(),productId);
//3.減庫存(模拟在記憶體(或redis)中減庫存)
stockNum =stockNum-1;
try {
//4.模拟一些IO或其他業務操作
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
stock.put(productId,stockNum);
}
//解鎖
redisLock.unlock(productId,String.valueOf(time));
}