天天看點

redisson 看門狗_redisson用戶端實作Redis可重入鎖與公平鎖一、可重入鎖(Reentrant Lock)

redisson實作了分布式和可擴充的java資料結構,支援的資料結構有:List, Set, Map, Queue, SortedSet, ConcureentMap, Lock, AtomicLong, CountDownLatch。并且是線程安全的,底層使用Netty 4實作網絡通信。和jedis相比,功能比較簡單,不支援排序,事務,管道,分區等redis特性,可以認為是jedis或lettuce的補充,不能替換jedis或lettuce。

如果需要分布式鎖,分布式集合等分布式的進階特性,添加Redisson結合使用,因為Redisson本身對字元串的操作支援很差。

在一些高并發的場景中,比如秒殺,搶票,搶購這些場景,都存在對核心資源,商品庫存的争奪,控制不好,庫存數量可能被減少到負數,出現超賣的情況,或者 産生唯一的一個遞增ID,由于web應用部署在多個機器上,簡單的同步加鎖是無法實作的,給資料庫加鎖的話,對于高并發,1000/s的并發,資料庫可能由行鎖變成表鎖,性能下降會厲害。那相對而言,redis的分布式鎖,相對而言,是個很好的選擇,redis官方推薦使用的Redisson就提供了分布式鎖和相關服務。

我們接下來介紹Redisson實作可重入鎖與公平鎖。

一、可重入鎖(Reentrant Lock)

以打水為例子說明可重入鎖,有多個人在排隊打水,此時管理者允許鎖和同一個人的多個水桶綁定。這個人用多個水桶打水時,第一個水桶和鎖綁定并打完水之後,第二個水桶也可以直接和鎖綁定并開始打水,所有的水桶都打完水之後打水人才會将鎖還給管理者。這個人的所有打水流程都能夠成功執行,後續等待的人也能夠打到水。這就是可重入鎖。

redisson 看門狗_redisson用戶端實作Redis可重入鎖與公平鎖一、可重入鎖(Reentrant Lock)

但如果是非可重入鎖的話,此時管理者隻允許鎖和同一個人的一個水桶綁定。第一個水桶和鎖綁定打完水之後并不會釋放鎖,導緻第二個水桶不能和鎖綁定也無法打水。目前線程出現死鎖,整個等待隊列中的所有線程都無法被喚醒。

redisson 看門狗_redisson用戶端實作Redis可重入鎖與公平鎖一、可重入鎖(Reentrant Lock)

基于Redis的Redisson分布式可重入鎖RLock Java對象實作了java.util.concurrent.locks.Lock接口。

RLock lock = redisson.getLock("anyLock");// 最常見的使用方法lock.lock();
           

大家都知道,如果負責儲存這個分布式鎖的Redisson節點當機以後,而且這個鎖正好處于鎖住的狀态時,這個鎖會出現鎖死的狀态。為了避免這種情況的發生,Redisson内部提供了一個監控鎖的看門狗,它的作用是在Redisson執行個體被關閉前,不斷的延長鎖的有效期。預設情況下,看門狗的檢查鎖的逾時時間是30秒鐘,也可以通過修改Config.lockWatchdogTimeout來另行指定。

另外Redisson還通過加鎖的方法提供了leaseTime的參數來指定加鎖的時間。超過這個時間後鎖便自動解開了。

// 加鎖以後10秒鐘自動解鎖// 無需調用unlock方法手動解鎖lock.lock(10, TimeUnit.SECONDS);​// 嘗試加鎖,最多等待100秒,上鎖以後10秒自動解鎖boolean res = lock.tryLock(100, 10, TimeUnit.SECONDS);if (res) {   try {     ...   } finally {       lock.unlock();   }}
           

二. 公平鎖(Fair Lock)

公平鎖是指多個線程按照申請鎖的順序來擷取鎖,線程直接進入隊列中排隊,隊列中的第一個線程才能獲得鎖。公平鎖的優點是等待鎖的線程不會餓死。缺點是整體吞吐效率相對非公平鎖要低,等待隊列中除第一個線程以外的所有線程都會阻塞,CPU喚醒阻塞線程的開銷比非公平鎖大。

非公平鎖是多個線程加鎖時直接嘗試擷取鎖,擷取不到才會到等待隊列的隊尾等待。但如果此時鎖剛好可用,那麼這個線程可以無需阻塞直接擷取到鎖,是以非公平鎖有可能出現後申請鎖的線程先擷取鎖的場景。非公平鎖的優點是可以減少喚起線程的開銷,整體的吞吐效率高,因為線程有幾率不阻塞直接獲得鎖,CPU不必喚醒所有線程。缺點是處于等待隊列中的線程可能會餓死,或者等很久才會獲得鎖。

直接用語言描述可能有點抽象,這裡作者用從别處看到的一個例子來講述一下公平鎖和非公平鎖。

redisson 看門狗_redisson用戶端實作Redis可重入鎖與公平鎖一、可重入鎖(Reentrant Lock)

如上圖所示,假設有一口水井,有管理者看守,管理者有一把鎖,隻有拿到鎖的人才能夠打水,打完水要把鎖還給管理者。每個過來打水的人都要管理者的允許并拿到鎖之後才能去打水,如果前面有人正在打水,那麼這個想要打水的人就必須排隊。管理者會檢視下一個要去打水的人是不是隊伍裡排最前面的人,如果是的話,才會給你鎖讓你去打水;如果你不是排第一的人,就必須去隊尾排隊,這就是公平鎖。

但是對于非公平鎖,管理者對打水的人沒有要求。即使等待隊伍裡有排隊等待的人,但如果在上一個人剛打完水把鎖還給管理者而且管理者還沒有允許等待隊伍裡下一個人去打水時,剛好來了一個插隊的人,這個插隊的人是可以直接從管理者那裡拿到鎖去打水,不需要排隊,原本排隊等待的人隻能繼續等待。如下圖所示:

redisson 看門狗_redisson用戶端實作Redis可重入鎖與公平鎖一、可重入鎖(Reentrant Lock)

基于Redis的Redisson分布式可重入公平鎖也是實作了java.util.concurrent.locks.Lock接口的一種RLock對象。它保證了當多個Redisson用戶端線程同時請求加鎖時,優先配置設定給先送出請求的線程。所有請求線程會在一個隊列中排隊,當某個線程出現當機時,Redisson會等待5秒後繼續下一個線程,也就是說如果前面有5個線程都處于等待狀态,那麼後面的線程會等待至少25秒。

RLock fairLock = redisson.getFairLock("anyLock");// 最常見的使用方法fairLock.lock();
           

大家都知道,如果負責儲存這個分布式鎖的Redis節點當機以後,而且這個鎖正好處于鎖住的狀态時,這個鎖會出現鎖死的狀态。為了避免這種情況的發生,Redisson内部提供了一個監控鎖的看門狗,它的作用是在Redisson執行個體被關閉前,不斷的延長鎖的有效期。預設情況下,看門狗的檢查鎖的逾時時間是30秒鐘,也可以通過修改Config.lockWatchdogTimeout來另行指定。

另外Redisson還通過加鎖的方法提供了leaseTime的參數來指定加鎖的時間。超過這個時間後鎖便自動解開了。

// 10秒鐘以後自動解鎖// 無需調用unlock方法手動解鎖fairLock.lock(10, TimeUnit.SECONDS);// 嘗試加鎖,最多等待100秒,上鎖以後10秒自動解鎖boolean res = fairLock.tryLock(100, 10, TimeUnit.SECONDS);...fairLock.unlock();
           

繼續閱讀