一、 測試
@Autowired
private RedisLockHelper redisLockHelper;
private static final String LOCK ="lock:equipment";
// 14: 48執行
//@Scheduled(cron = "0 48 14 ? * *")
public void orderSync() {
long time = System.currentTimeMillis() + (10*1000);
//進行加鎖操作
if (redisLockHelper.lock(LOCK, time)) {
System.out.println("加鎖");
}else {
System.out.println("解鎖");
redisLockHelper.unlock(LOCK,time);
}
}
二、操作redis的工具類
package com.kmnfsw.util;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;
/**
*
* @author wyg
*/
@Component
@Slf4j
public class RedisLockHelper {
@Autowired
private RedisTemplate redisTemplate;
/**
* 加鎖
* @param target 唯一標誌
* @param timeStamp 當前時間+超時時間 也就是時間戳
* @return
*/
public boolean lock(String target, long timeStamp){
// 如果鍵不存在則新增,存在則不改變已經有的值。
if(redisTemplate.opsForValue().setIfAbsent(target,timeStamp)){
return true;
}
// 判斷鎖超時 - 防止原來的操作異常,沒有運行解鎖操作 防止死鎖
long currentLock = (long) redisTemplate.opsForValue().get(target);
// 如果鎖過期 currentLock不爲空且小於當前時間
if(currentLock < System.currentTimeMillis()){
// 獲取上一個鎖的時間value 對應getset,如果lock存在 設置給過來的值,並返回舊的值
long preLock = (long) redisTemplate.opsForValue().getAndSet(target,timeStamp);
// 假設兩個線程同時進來這裏,因爲key被佔用了,而且鎖過期了。獲取的值currentLock=A(get取的舊的值肯定是一樣的),兩個線程的timeStamp都是B,key都是K.鎖時間已經過期了。
// 而這裏面的getAndSet一次只會一個執行,也就是一個執行之後,上一個的timeStamp已經變成了B。只有一個線程獲取的上一個值會是A,另一個線程拿到的值是B。
if(preLock==currentLock){
// preLock不爲空且preLock等於currentLock,也就是校驗是不是上個對應的商品時間戳,也是防止併發
return true;
}
}
return false;
}
/**
* 解鎖
* @param target
* @param timeStamp
*/
public void unlock(String target,long timeStamp){
try {
long currentValue = (long) redisTemplate.opsForValue().get(target);
if( currentValue<=timeStamp){
// 刪除鎖狀態
redisTemplate.opsForValue().getOperations().delete(target);
}
} catch (Exception e) {
log.error("警報!警報!警報!解鎖異常{}",e);
}
}
}