實現分布式鎖的必要條件:互斥性和不會發生死鎖
互斥性的保證:就是同時只能有乙個執行緒註冊成功獲取到鎖 比如 jedis.setnx(key,value):方法含義:如果key不存在就設定
避免發生死鎖:就是獲得鎖以後 無論這個加鎖的客戶端怎麼樣,都要最終能釋放出來鎖;
redis的分布式鎖的實現機制就是:
獲得鎖:多執行緒競爭註冊相同的key並儲存value,因為jedis有排他性的方法比如setnx(key,value),如果不存在對應的key,就註冊key和value,所以同時只會有乙個執行緒搶註成功(排他性)。
然後註冊成功的執行緒就去執行自己要執行的業務**。
釋放鎖:就是刪除key,刪除key的時候,通過key先獲得value,然後執行緒根據value的值來判斷這個key是不是自己註冊的,如果是就可以刪除key。所以value的值的識別性很重要。
釋放鎖以後(key被刪除),其餘的執行緒就可以開始搶註key了,就重複上演1,2,3流程了。
綜上:我們可以理解為 誰能註冊key 就相當於誰獲得了鎖,至於是誰註冊的, 就要根據註冊key的時候的value的值來判斷。所以不同執行緒在競爭鎖的時候key值應該一樣,value值應該能識別執行緒身份,比如value是執行緒的名字。
獲取鎖的過程中存在的問題
避免死鎖
如果某個執行緒,獲得了鎖(註冊key和value)之後,突然死掉了,怎麼辦?這把鎖就永遠沒辦法得到主動釋放,所以我們要設定鎖的過期時間,
讓鎖被動釋放,我們可以給key設定過期時間,過期了,key就會被redis刪除掉,從而被動釋放了鎖。
if(jedis.setnx(key, value) ==1)
上面**這樣寫符合我們的要求嗎?搶占了,也設定了過期時間,是不是就ok了,
答案是不行的,因為萬一執行了jedis.setnx(key, value) ==1之後,執行緒掛了,怎麼辦?是不是key就沒有過期時間了,就死鎖了,
所以我們需要將設定key和過期時間放在乙個原子操作裡,而上面分兩步是兩個原子操作。所以我們可以選擇。
jedis.set(key,value,"nx", "px",expiretime);這裡就是將設定key和過期時間放在乙個原子操作裡了。12
3456
789綜上獲取鎖的**可以如下:
public boolean lock1(keyprefix prefix, string key, string value, long lockexpiretimeout,
long lockwaittimeout)
lockwaittimeout = deadtimeline - system.currenttimemillis();
if (lockwaittimeout <= 0l)
}} catch (exception ex) finally
return false;}1
2345
6789
1011
1213
1415
1617
1819
2021
2223
2425
2627
2829
釋放鎖存在的問題
錯誤釋放鎖
僅僅 jedis.del(key) 肯定是不行的,比如此時key是執行緒a註冊的,執行緒b呼叫這個方法會直接刪除這個key,就相當於b執行緒釋放了a執行緒的鎖。
那加個判斷呢?判斷value是否是自己放進去的,如果不是就不能刪除。
string currentvalue = jedis.get(realkey);
if (!stringutils.isempty(currentvalue) && value.equals(currentvalue))
看上去上面的**好像沒什麼問題,但是仔細一看,這裡的判斷和刪除是兩步操作,也就是說可能存在一種情況,a執行緒在釋放自己的鎖的時候,剛執行完
!stringutils.isempty(currentvalue) && value.equals(currentvalue),準備刪除自己註冊的key的時候,這個時候剛好key過期了,
b執行緒搶註了自己的key和value,此時按理說鎖是b的,但是a執行緒繼續執行jedis.del(realkey)就會刪除b的key,從而釋放了b的鎖這樣也是不行的。
所以我們就需要 將判斷和刪除 這兩步操作合併成乙個原子操作,那怎麼辦呢?通過lua指令碼來做如下:
public boolean unlock1(keyprefix prefix, string key, string value)
} catch (exception ex) finally
return false;}1
2345
6789
1011
1213
1415
1617
1819
2021
2223
2425
2627
2829
3031
3233
34
分布式鎖 使用Redis實現分布式鎖
關於分布式鎖的實現,我的前一篇文章講解了如何使用zookeeper實現分布式鎖。關於分布式鎖的背景此處不再做贅述,我們直接討論下如何使用redis實現分布式鎖。關於redis,筆主不打算做長篇大論的介紹,只介紹下redis優秀的特性。支援豐富的資料型別,如string list map set zs...
redis實現分布式鎖
隨便 系統越來越大,各功能模組除了垂直切割以外,同時也得做集群處理,那麼問題來了,在多執行緒情況下對於資源的競爭就需要乙個統一的訪問限制。以選課系統為例子,集群中各節點對課程可選數量同時操作,這裡就需要同步了,否則會導致最後選到的數量比可選的數量大,這裡我們的分布式鎖就派上用場了。利用redis來實...
redis實現分布式鎖
分布式鎖可以基於很多種方式實現,比如zookeeper redis.不管哪種方式,他的 基本原理是不變的 用乙個狀態值表示鎖,對鎖的占用和釋放通過狀態值來標識。1 使用redis的setnx命令實現分布式鎖 1 實現的原理 redis為單程序單執行緒模式,採用佇列模式將併發訪問變成序列訪問,且多客戶...