redis分散式鎖的設計和使用demo
package com.nchu.common.utils;
import lombok.extern.slf4j.Slf4j;
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
@Slf4j
public class RedisLock {
@Autowired
private StringRedisTemplate redisTemplate;
/**
* 加鎖
* @param key
* @param value 當前時間+超時時間
* @return
*/
public boolean lock(String key, String value) {
//setIfAbsent 可以設定key和value 則返回true 否則返回false key是商品id value值為當前時間+超時時間
if(redisTemplate.opsForValue().setIfAbsent(key, value)) {
return true;
}
String currentValue = redisTemplate.opsForValue().get(key);
//如果鎖過期
if (!StringUtils.isEmpty(currentValue)
&& Long.parseLong(currentValue) < System.currentTimeMillis()) {
//獲取上一個鎖的時間 並把當前最新的時間+超時時間放進去
String oldValue = redisTemplate.opsForValue().getAndSet(key, value);
//最後再判斷一次 這期間沒有人去加過鎖 因為當鎖過期的時候很可能進去多個執行緒執行到這 這個時候的時間還是之前取到的時間 說明自己加鎖成功
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);
}
}
}
使用
/**
* 使用者付款時候減去庫存
*/
private int subStock(String OrderId) {
List<OrderDetail> orderDetailList = orderDetailMapper.selectByOrderId(OrderId);
List<String> ProductIds = orderDetailList.stream().map(x -> x.getProductId()).collect(Collectors.toList());
Map<String, OrderDetail> productIdAndOrderDetail = new HashMap<>();
orderDetailList.forEach(x -> productIdAndOrderDetail.put(x.getProductId(), x));
for (String id : ProductIds) {
// 分散式鎖 加鎖
Long currentDate = System.currentTimeMillis() + 5000; //設定加鎖時間
Boolean result = redisLock.lock(id, currentDate.toString());
//不成功重試
while (!result) {
currentDate = System.currentTimeMillis() + 5000; //設定加鎖時間
result = redisLock.lock(id, currentDate.toString());
}
ProductInfo productInfo = productInfoMapperEx.selectByPrimaryKey(id);
OrderDetail orderDetail=productIdAndOrderDetail.get(id);
Integer nowStockNum = productInfo.getProductStock() - orderDetail.getProductQuantity();
if (nowStockNum <= 0) { //小於0 說明庫存不足
return -1;
}
productInfo.setProductStock(nowStockNum);
//更新庫存
productInfoMapperEx.updateByPrimaryKey(productInfo);
//解鎖
redisLock.unlock(id, currentDate.toString());
}
return 1;
}