偽代碼如下:
//整體流程
public void acquireLockMethod(){
Map<String,Object> lockMap = new HashMap<>();
try{
lockMap = acquireLock(lockKey,expire);
if(lockMap!=null){
todoBusiness();//獲得鎖著蛙,執(zhí)行業(yè)務(wù)邏輯方法
}
}catch(Exception e){
throw e;
}finally{
releaseLock(lockKey,lockMap);
}
}
//acquireLock()方法:
public Map<String,Object> acquireLock(String lock,long expired){
Map<String,Object> map = new HashMap<>();
long value = System.currentTimeMillis() + expired + 1;
long acquired = jedis.setnx(lock, String.valueOf(value));
if (acquired == 1)
map.put("isSuccess",true);
map.put("expireTimeStr":value);
return map;
else {
long oldValue = Long.valueOf(jedis.get(lock));
//如果其他資源之前獲得鎖已經(jīng)超時
if (oldValue < System.currentTimeMillis()){
String getValue = jedis.getSet(lock, String.valueOf(value));
//上一個鎖超時后會有很多線程去爭奪鎖,所以只有拿到oldValue的線程才是獲得鎖的甸箱。
if (Long.valueOf(getValue) == oldValue)
map.put("isSuccess",true);
map.put("expireTimeStr":value);
return map;
else
return null;
}
else return null ;
}
//解鎖
private void release(ShardedJedis jedis, String lockKey,Map<String,Object> lockMap) {
if(CollectionUtils.isEmpty(lockMap)){
return;
}
Boolean locked = (Boolean) lockMap.get("isSuccess");
String lockExpiresStr = (String) lockMap.get("expireTimeStr");
if (locked) {
String oldValueStr = jedis.get(lockKey);
if (oldValueStr != null) {
// 競爭的 redis.getSet 導(dǎo)致其時間跟原有的由誤差,若誤差在 超時范圍內(nèi)挣输,說明仍舊是 原來的鎖
Long diff = Long.parseLong(lockExpiresStr) - Long.parseLong(oldValueStr);
if (diff < expireMsecs) {
jedis.del(lockKey);
} else {
// 這個進程的鎖超時了,被 新的進程鎖獲得替換了站楚。則不進行任何操作良蛮。打印日志蠢棱,方便后續(xù)跟進
log.error("the lockKey over time.lockKey:{}.expireMsecs:{},over time is", lockKey, expireMsecs, System.currentTimeMillis() - Long.valueOf(lockExpiresStr));
}
}
}
}
此分布式鎖:
利用redis本身的單線程特性袭灯,以及setNX和getSet方法的原子特性髓介,保證了多請求的加鎖的互斥性,最后只有一個請求獲得鎖病毡。
可以通過使用while循環(huán)實現(xiàn)阻塞特定時間的鎖烁落。
通過getSet方法,沒有使用expire方法,保證了此分布式鎖不會造成死鎖。
此分布式鎖依舊存在的問題:
- 如果業(yè)務(wù)邏輯執(zhí)行時間超時(鎖此時自動失效了)嗦董,此時業(yè)務(wù)邏輯的數(shù)據(jù)已經(jīng)不安全经窖。雖然在解鎖的時候,判斷了鎖的value值,做到了不會錯誤釋放鎖的情況窖壕,但是對超時業(yè)務(wù)邏輯只是做了日志記錄呕乎,不太靠譜。
- redis集群部署時,這樣的分布式鎖會存在什么問題嗎。這個沒有試驗過。