如果沒有用到分布式鎖的場景是這樣的:
這是乙個普通的更新使用者年齡的功能,各層**如下,訪問controller層,乙個更新,乙個查詢:
這是service層,我們使用contdownlatch發令槍來模擬線程同時併發的情況,發令槍設為32,即32個執行緒同時去請求修改年齡,
這裡使用執行緒池來提交多執行緒任務,看**知道,這裡我們已經有了判斷年齡的操作,當查詢使用者查詢大於0時,才去調更新使用者年齡-1的方法,等下看看有沒有用
這裡是sql,可以看到兩個sql,乙個查詢使用者年齡,乙個會執行使用者年齡每次減1 ,
這裡是使用者資料,我們可以看到,使用者uid為ur12324的使用者,他的年齡是30,接著我們來調32個執行緒來操作減他年齡
我們請求下這個方法:
可以看到庫中年齡已被減為-2,在未加鎖的情況下,查詢較驗並沒有什麼作用,此時如果加個synchronized或lock鎖肯定能避免這種情況,但我們本文討論的是多例項或分布式環境中,此加鎖方式仍然會產生問題,感興趣的可以試下是不是
下面我們開始實現乙個redis分布式鎖,來避免這種情況發生,先說說實現思路:
2.其它執行緒試著訪問拿出它本地變數與redis中某key進行比較,如果不一致,則說明有鎖,此執行緒休眠一段時間,再試著加鎖;
3.加鎖成功的執行緒在操作結束後刪掉它持有鎖(用lua實現,保證原子性,在它比對和刪除鎖的過程中,其它執行緒不會加鎖成功),讓其它執行緒再次加鎖以執行任務;
說明:鎖的時間為200ms可預防執行緒掛掉之後死鎖,200ms後會自動釋放
下面看看我們寫的鎖**:
片段1:使用redislock 實現lock來複寫它的方法
片段2:試著加鎖的方法:
片段3:解鎖方法,此處首先從執行緒本地變數獲取它的隨機數,然後呼叫lua指令碼,與redis中key相比較,如果相同則刪除,否則返回0;
此為lua指令碼方法,用此方法可以保證判斷和刪除的原子性,在此過程中沒有執行緒可以操作此key:
到此為止,我們鎖基本寫完,來測試下有沒有用:
我們在此方法前後分別加入加鎖和解鎖方法,使用方式和lock鎖一樣, 我們重新把年齡恢復到30後來測試一下吧
先看看日誌:
這裡可以看到各個執行緒爭奪鎖的情況,再看看執行結果
這裡我們可以看到雖然是32個執行緒併發執行,但此值並不會變為負數,加鎖成功.
我們可以看到最後2個執行緒並沒有執行方法
此時說明加鎖成功,大家可以在分布式環境中測試更明顯,有關極端情況下解鎖失敗後應該做什麼也可以由我們自己決定,比redission要靈活,帶鎖的redis最好是單例項,在集群中可能會出問題,有機會我們再用zk實現下.
redis分布式鎖
redis分布式鎖 直接上 我寫了四個redis分布式鎖的方法,大家可以提個意見 第一種方法 redis分布式鎖 param timeout public void lock long timeout thread.sleep 100 catch exception e override publi...
Redis分布式鎖
分布式鎖一般有三種實現方式 1.資料庫樂觀鎖 2.基於redis的分布式鎖 3.基於zookeeper的分布式鎖.首先,為了確保分布式鎖可用,我們至少要確保鎖的實現同時滿足以下四個條件 互斥性。在任意時刻,只有乙個客戶端能持有鎖。不會發生死鎖。即使有乙個客戶端在持有鎖的期間崩潰而沒有主動解鎖,也能保...
redis分布式鎖
使用redis的setnx命令實現分布式鎖 redis為單程序單執行緒模式,採用佇列模式將併發訪問變成序列訪問,且多個客戶端對redis的連線並不存在競爭關係。redis的setnx命令可以方便的實現分布式鎖。setnx key value 將key的值設為value,當且僅當key不存在。如給定的...