一文詳解Redisson分布式鎖底層實現(xiàn)原理
在Java中有很多保證線程安全的方式,比如synchorized,lock鎖等等,這些在單機環(huán)境下都能發(fā)揮不錯的作用,但是在分布式的環(huán)境下,這些機制就會失去大部分的作用。
在分布式環(huán)境下就需要引入分布式鎖,實現(xiàn)分布式鎖的方式有好多種,比如redis、zookeeper,或者通過數(shù)據庫來實現(xiàn),但是在分布式的情況下還需要考慮機器宕機的情況,如果某臺機器上的線程獲取到了這個鎖,但此時機器宕機了。那么就沒辦法去釋放,就會造成死鎖的情況。
為了避免這種情況,就需要給鎖加上一個過期時間,而過期時間的設定又是一個令人非常頭疼的問題。
在Redisson種有一個看門狗機制,它給出了一種過期時間的很好的解決辦法。下面就來研究一下它具體實現(xiàn)吧。
Redisson的加鎖入口是tryLock(),此方法需提供獲取鎖的等待時間,如果在規(guī)定時間內未搶到鎖,會返回false。
這里可以看到tryLock()方法實際上是調用了下面這個方法,這里給了一個leaseTime的默認值,至于為什么是-1,我們接著往下看。
進來之后會發(fā)現(xiàn),這個方法的核心就是執(zhí)行一個tryAcquire方法,我們點進去看一下。
tryAcquire方法實際會去執(zhí)行tryAcquireAsync異步的去獲取鎖,然后再使用get獲取結果,如果結果為null代表獲取鎖成功,這里后面會講。
然后進到tryAcquireAsync方法,在這里判斷了leaseTime是不是-1,如果我們自己設定了過期時間,那么就會以我們設置的為準,并且不會去開啟自動續(xù)期。
如果是默認的-1,那么異步獲取鎖之后,后面還會去開啟一個自動續(xù)期的定時任務。
異步獲取鎖是通過tryLockInnerAsync這個方法實現(xiàn)的。第一個參數(shù)是30000,傳入的是commandExecutor.getConnectionManager().getCfg().getLockWatchdogTimeout(),點進去可以看到
在這個方法里使用lua腳本的方式去執(zhí)行了set操作
這段lua腳本的意思是:
鎖不存在,加鎖成功,設置hash數(shù)據結構鎖: 鎖名 -> 加鎖線程:id -> 加鎖次數(shù)(1)
鎖存在且是本線程的鎖 加鎖次數(shù)增加:鎖名 -> 加鎖線程:id -> 加鎖次數(shù)+1
鎖存在且不是本線程的鎖 加鎖失敗 返回鎖剩余過期時間
從這里也可以體現(xiàn)出此鎖的可重入,即某一線程獲取到鎖之后,那么這個線程再去獲取該鎖的話也可以成功
同時也可以如果返回為null那么說明獲取鎖成功。
然后后面會判斷如果結果為null,就會去執(zhí)行scheduleExpirationRenewal(threadId)方法,進去看一下
由于我們的Redission的分布式鎖是可重入鎖,所以這里會首先判斷一下是不是第一次加鎖,如果不是第一次則加鎖次數(shù)加 1 不會再開啟續(xù)期 因為第一次加鎖時調用
如果是第一次加鎖的話就回去調用renewExpiraton()去開啟自動續(xù)期。
addThreadId:重入次數(shù)+1
renewExpiraton()開啟自動續(xù)期這個方法里面創(chuàng)建了一個定時任務,主要邏輯是通過renewExpirationAsync(threadId)方法去執(zhí)行續(xù)期邏輯,執(zhí)行成功后還會通過下面if (res) {renewExpiration();}方法遞歸調用。
注意到這個線程執(zhí)行的間隔是internalLockLeaseTime / 3,也就是30 / 3 = 10s
我們可以看一下renewExpirationAsync方法里面的邏輯
此lua腳本的意思是:當前線程持有的鎖是否還存在 存在的話重新設置鎖的過期時間(默認 30 秒)
至此加鎖的邏輯就追完了。
下面我們看一看釋放鎖的邏輯。其入口為:unlock方法,它會去調用unlockAsync方法。
unlockAsync里面掉了unlockInnerAsync方法去釋放鎖,
unlockInnerAsync方法點進去我們可以看到它也是通過lua腳本的方式去釋放鎖。
若鎖不存在 返回 若鎖存在 加鎖次數(shù) -1 若加鎖次數(shù)仍不等于 0 (可重入),重新設置鎖的過期時間,返回 若加鎖次數(shù)減為 0,刪除鎖,同步發(fā)布釋放鎖事件,返回
以上就是一文詳解Redisson分布式鎖底層實現(xiàn)原理的詳細內容,更多關于Redisson分布式鎖實現(xiàn)原理的資料請關注腳本之家其它相關文章!
相關文章
如何使用Spring RestTemplate訪問restful服務
這篇文章主要介紹了如何使用Spring RestTemplate訪問restful服務,詳細的介紹了什么是RestTemplate以及簡單實現(xiàn),非常具有實用價值,需要的朋友可以參考下2018-10-10redis之基于SpringBoot實現(xiàn)Redis stream實時流事件處理方式
這篇文章主要介紹了redis之基于SpringBoot實現(xiàn)Redis stream實時流事件處理方式,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2023-06-06詳解Spring中singleton?bean如何同時服務多個請求
這篇文章主要介紹了詳解Spring中singleton?bean如何同時服務多個請求2023-02-02Spring中@Service注解的作用與@Controller和@RestController之間區(qū)別
這篇文章主要介紹了Spring中@Service注解的作用與@Controller和@RestController之間的區(qū)別,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習吧2023-03-03應用啟動數(shù)據初始化接口CommandLineRunner和Application詳解
這篇文章主要介紹了應用啟動數(shù)據初始化接口CommandLineRunner和Application詳解,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2021-12-12Java如何利用CompletableFuture描述任務之間的關系
Java如何根據線程的執(zhí)行結果執(zhí)行下一步動作呢,F(xiàn)uture的另一個實現(xiàn)類CompletableFuture能夠優(yōu)雅的解決異步化問題,下面就跟隨小編一起了解一下吧2023-07-07mybatis實現(xiàn)動態(tài)升降序的問題小結
文章介紹了如何在MyBatis的XML文件中實現(xiàn)動態(tài)排序,使用$符號而不是#符號來引用變量,以避免SQL注入,同時,強調了在Java代碼中進行防注入處理的重要性,感興趣的朋友一起看看吧2025-02-02