1.方案一
首先傳入需要被鎖的資源id和當前操作的使用者userid,先判斷當前redis中是否有key為id的資料,如果存在,直接返回false代表該資源已經被人使用;如果不存在再插入鍵為id,值為userid,有效時間30s的資料,並且返回true。
/**
* 使用者進入獲取某個key的鎖
* 返回false說明獲取失敗,返回true說明成功
* @return*/(
"getlock1"
)@responsebody
public
boolean
getlock1
(integer id, string userid)
//在redis中插入一條鍵為id,值為userid,有效時間30s的資料
stringredistemplate.
opsforvalue()
.set
(id.
tostring()
, userid,
30, timeunit.seconds)
;return
true
;}
該方法其實存在漏洞,比如當兩個使用者同時進入搶占同一資源,在同一時間查詢到redis中不存在鍵為id的資料,即認為沒有人在使用當前資源,所以都去set資料,而set是可以覆蓋的,導致兩個使用者都看起來上鎖成功了,都會返回true。模擬一下這種情況:
public
class
test
implements
runnable
@override
public
void
run(
)}
2.方案二
redis本身在set資料時有乙個方法是如果不存在才能插入成功,否則會插入失敗,查閱stringredistemplate的api發現有乙個setifabsent()方法,修改後的**:(
"getlock2"
)@responsebody
public
boolean
getlock2
(integer id, string userid)
//在redis中插入一條鍵為id,值為userid,有效時間30s的資料,使用setifabsent可以在不存在該key的情況下完成插入
boolean res = stringredistemplate.
opsforvalue()
.setifabsent
(id.
tostring()
, userid,
30, timeunit.seconds)
;return res;
}測試結果:
在拿到鎖真正執行邏輯**時,一般會在執行結束後將redis中的資料進行刪除,達到釋放鎖的目的,詳見方案三。
3.方案三
我們在操作之前先呼叫方案二中的getlock方法。成功後,再執行邏輯**,我使用sleep()模擬邏輯**操作所需要的時間,在finally**塊中將鎖釋放。(
"option1"
)@responsebody
public
void
option1
(integer id, string userid)
else
}catch
(interruptedexception e)
finally
else}}
}).start()
;}}使用上面的多執行緒測試工具,將訪問路徑替換為option1,訪問後控制台列印如下:
其實這樣做還有問題,因為真正邏輯進行操作時不一定是10s,有可能是20s,30s,甚至是超過開始設定的redis有效時間(上面設定了30s),所以就存在邏輯**執行的時間超過了有效時間,自動釋放了鎖,其他人在此時就可以拿到鎖,而在邏輯**執行完之後又執行了finally**塊,把別人的鎖釋放掉了,解決方案見方案四。4.方案四
在加鎖的時候設定隨機值,並存放到redis中,釋放鎖的時候匹配到該隨機值才可以釋放鎖。要加入隨機值的話,redis的value使用hash格式進行儲存較為合理,所以修改後的getlock()方法如下:
public
boolean
getlock3
(integer id, string userid, string radom)
//在redis中插入一條鍵為id,值為userid和隨機值組成的hash,有效時間30s的資料,使用putifabsent可以在不存在該key的情況下完成插入
if(stringredistemplate.
opsforhash()
.putifabsent
(id.
tostring()
,"userid"
, userid)
)return
false
;}
redis視覺化工具檢視效果:
獲得鎖的時候將隨機值儲存成變數,最後在finally中判斷變數和redis中儲存的隨機值是否相同,不相同的話不允許釋放鎖,將執行緒中的sleep時間設定成40s,鎖自動失效時間是30s,就會發生上述的情況,修改後的**如下:(
"option2"
)@responsebody
public
void
option2
(integer id, string userid)
else
}catch
(interruptedexception e)
finally
else
}else}}
}).start()
;}}測試之後控制台列印如下:
鎖已經不屬於當前使用者,但是邏輯**還沒執行完,所以需要加乙個判斷來延續鎖,見方案五。5.方案五
在邏輯**中加入每隔10s檢測是否超時時間小於等於20s,是的話重新設定超時時間30s,防止鎖失效,在這裡借用上面倒計時**,判斷i是否是10的整數倍來實現10s判斷一次
鎖延續成功,並且最後鎖也是屬於自己的,釋放成功。至此,redis分布式鎖的實現就較為完善了。
分布式鎖 使用Redis實現分布式鎖
關於分布式鎖的實現,我的前一篇文章講解了如何使用zookeeper實現分布式鎖。關於分布式鎖的背景此處不再做贅述,我們直接討論下如何使用redis實現分布式鎖。關於redis,筆主不打算做長篇大論的介紹,只介紹下redis優秀的特性。支援豐富的資料型別,如string list map set zs...
redis實現分布式鎖
隨便 系統越來越大,各功能模組除了垂直切割以外,同時也得做集群處理,那麼問題來了,在多執行緒情況下對於資源的競爭就需要乙個統一的訪問限制。以選課系統為例子,集群中各節點對課程可選數量同時操作,這裡就需要同步了,否則會導致最後選到的數量比可選的數量大,這裡我們的分布式鎖就派上用場了。利用redis來實...
redis實現分布式鎖
分布式鎖可以基於很多種方式實現,比如zookeeper redis.不管哪種方式,他的 基本原理是不變的 用乙個狀態值表示鎖,對鎖的占用和釋放通過狀態值來標識。1 使用redis的setnx命令實現分布式鎖 1 實現的原理 redis為單程序單執行緒模式,採用佇列模式將併發訪問變成序列訪問,且多客戶...