Redisson 四大核心機制實現原理詳解

一、可重入鎖(Reentrant Lock)

可重入鎖是什么?

  • 通俗定義

    可重入鎖類似于一把“智能鎖”,它能識別當前的鎖持有者是否是當前線程:

    • 如果是,則允許線程重復獲取鎖(重入),并記錄重入次數。
    • 如果不是,則其他線程必須等待鎖釋放后才能獲取。
  • 典型場景

    當一個線程調用了一個被鎖保護的方法A,而方法A內部又調用了另一個被同一鎖保護的方法B時,如果鎖不可重入,線程會在調用方法B時被自己阻塞(死鎖)。可重入鎖允許這種嵌套調用。

public class Demo {private final Lock lock = new SomeLock(); // 假設這是一個鎖public void methodA() {lock.lock();try {methodB(); // 調用另一個需要加鎖的方法} finally {lock.unlock();}}public void methodB() {lock.lock();try {// 業務邏輯} finally {lock.unlock();}}
}
  • 如果鎖不可重入 線程進入methodA獲取鎖后,調用methodB時再次嘗試加鎖,會因為鎖已被自己持有而永久阻塞(死鎖)。
  • 如果鎖可重入 線程在methodB中能成功獲取鎖,計數器從1增加到2,釋放時計數器遞減,最終正常釋放。

實現原理:通過 Redis 的 Hash 結構實現線程級鎖的可重入性。

  1. 數據結構

    • Key:鎖名稱(如 lock:order:1001)。
    • Field:客戶端唯一標識(UUID + 線程ID),如 b983c153-7091-42d8-823a-cb332d52d2a6:1
    • Value:鎖的 重入次數(初始為 1,重入時遞增)。
  2. 加鎖邏輯

    • 首次加鎖:執行 Lua 腳本,若 Key 不存在,創建 Hash 并設置重入次數為 1。
      -- KEYS[1]=鎖名, ARGV[1]=鎖超時時間, ARGV[2]=線程唯一ID
      if (redis.call('exists', KEYS[1]) == 0) then  	 -- 如果鎖不存在redis.call('hincrby', KEYS[1], ARGV[2], 1);  -- 創建Hash,記錄線程重入次數redis.call('pexpire', KEYS[1], ARGV[1]);	 -- 設置鎖超時時間return nil;									 -- 返回成功
      end;
      
    • 重入加鎖:若 Field 匹配當前線程,重入次數 +1。
      if (redis.call('hexists', KEYS[1], ARGV[2]) == 1) then	-- 如果鎖已被當前線程持有redis.call('hincrby', KEYS[1], ARGV[2], 1);			-- 增加重入次數redis.call('pexpire', KEYS[1], ARGV[1]);			-- 刷新鎖超時時間return nil;											-- 返回成功
      end;
      
  3. 釋放鎖:減少重入次數,歸零時刪除 Hash。

    -- KEYS[1]: 鎖名稱(如 my_lock)
    -- KEYS[2]: 發布訂閱的頻道名
    -- ARGV[1]: 解鎖消息標識(如 0)
    -- ARGV[2]: 鎖的過期時間(毫秒)
    -- ARGV[3]: 客戶端唯一標識(UUID + 線程ID)-- 檢查鎖是否存在且屬于當前線程
    if (redis.call('hexists', KEYS[1], ARGV[3]) == 0) thenreturn nil; -- 鎖不存在或不屬于當前線程,直接返回
    end;-- 減少重入計數器(原子操作)
    local counter = redis.call('hincrby', KEYS[1], ARGV[3], -1);if (counter > 0) then-- 仍有重入未釋放完,更新鎖過期時間redis.call('pexpire', KEYS[1], ARGV[2]);return 0; -- 返回0表示未完全釋放
    else-- 計數器歸零,刪除鎖并發布釋放通知redis.call('del', KEYS[1]);redis.call('publish', KEYS[2], ARGV[1]);return 1; -- 返回1表示鎖已完全釋放
    end;
    

二、鎖重試機制(Retry Mechanism)

重試機制的觸發條件

當調用 tryLock(long waitTime, long leaseTime, TimeUnit unit) 方法時,若 waitTime > 0,Redisson 會啟用重試機制。例如:

java// 10秒內不斷重試獲取鎖,獲取成功后持有鎖60秒
lock.tryLock(10, 60, TimeUnit.SECONDS);

若首次獲取鎖失敗,進入重試流程。

實現原理: 事件驅動優先,主動輪詢兜底

  1. 首次嘗試獲取鎖

    • 原子性操作:通過 Lua 腳本嘗試獲取鎖(檢查鎖是否存在或是否屬于當前線程)。
    • 失敗返回值:若鎖被其他線程持有,返回鎖的剩余存活時間(ttl)。
  2. 訂閱鎖釋放事件

    • 創建監聽頻道:訂閱 Redis 頻道 redisson_lock__channel:{lockName}
    • 事件驅動優化:避免頻繁輪詢,僅當鎖釋放時觸發重試,減少無效請求
    // 偽代碼:訂閱鎖釋放事件
    RFuture<RedissonLockEntry> future = subscribe(lockName);
    RedissonLockEntry entry = get(future);
    
  3. 循環重試(主動輪詢 + 事件觸發)

    • 計算剩余等待時間:基于 waitTime 和已消耗時間,動態調整剩余等待窗口。
    • 雙重檢測邏輯
      • 主動輪詢:定期(默認間隔 100ms ~ 300ms)執行 Lua 腳本嘗試獲取鎖。
      • 事件觸發:收到鎖釋放通知后立即嘗試獲取鎖。
    • 退避策略:每次重試失敗后,采用隨機遞增的等待時間(避免多個客戶端同時競爭導致雪崩)。

    關鍵代碼邏輯(簡化)

long remainingTime = waitTime; // 剩余等待時間
long startTime = System.currentTimeMillis();while (remainingTime > 0) {// 1. 嘗試獲取鎖Long ttl = tryAcquire(leaseTime, unit); // 調用Lua腳本if (ttl == null) {return true; // 獲取成功}// 2. 計算剩余時間long elapsed = System.currentTimeMillis() - startTime;remainingTime -= elapsed;if (remainingTime <= 0) {break; // 超時退出}// 3. 等待鎖釋放事件或超時entry.getLatch().tryAcquire(ttl, TimeUnit.MILLISECONDS); // 基于信號量等待// 4. 更新剩余時間remainingTime -= (System.currentTimeMillis() - startTime - elapsed);
}
return false; // 超時未獲取
  1. 超時終止
    • 時間窗口耗盡:若總耗時超過 waitTime,終止重試并返回失敗。
    • 資源清理:取消 Redis 訂閱,釋放連接。

三、WatchDog 看門狗(鎖續期機制)

防止業務執行時間超過鎖的過期時間,導致鎖提前釋放。

啟用看門狗需滿足以下條件之一:

  • 未顯式指定鎖的租約時間(leaseTime): 例如調用 lock.tryLock()lock.lock() 時不傳 leaseTime 參數。
  • 顯式設置租約時間為 -1: 例如 lock.tryLock(10, -1, TimeUnit.SECONDS)

注意:若指定了固定的 leaseTime(如 lock.tryLock(10, 30, TimeUnit.SECONDS)),看門狗不會啟動,鎖會在 30 秒后自動釋放。

實現原理:后臺線程自動續期鎖,防止業務未完成時鎖過期。

  1. 觸發條件:未指定鎖超時時間(如 lock.lock())。

  2. 續期邏輯

    • 定時任務:默認每 10 秒(lockWatchdogTimeout / 3)續期一次。

    • 續期命令:重置鎖的過期時間為 30 秒(默認值)。

      -- KEYS[1]: 鎖名稱
      -- ARGV[1]: 過期時間(默認30秒)
      -- ARGV[2]: 客戶端唯一標識
      if (redis.call('hexists', KEYS[1], ARGV[2]) == 1) thenredis.call('pexpire', KEYS[1], ARGV[1]);return 1;
      end;
      return 0;
      
  3. 終止條件

    • 鎖被釋放(unlock() 調用)。
    • 客戶端斷開連接或線程中斷。

四、主從一致性(MultiLock/RedLock)

Redis 主從復制是異步的,若主節點宕機且鎖未同步到從節點,可能導致多個客戶端同時持有鎖。

實現原理:基于多數派原則,向多個獨立節點加鎖。

  1. MultiLock 流程

    • 加鎖:向所有節點發送加鎖請求,需 半數以上成功(如 3 節點至少 2 個成功)。
    • 容錯:允許最多 ?(N-1)/2? 個節點故障(如 5 節點允許 2 個故障)。
    • 解鎖:無論加鎖是否成功,向所有節點發送解鎖命令。
  2. RedLock 算法增強

    • 時鐘同步:要求節點使用 NTP 同步時間,鎖有效期需包含時鐘漂移。
    • 加鎖驗證:計算加鎖耗時,確保有效時間未耗盡。
  3. 配置示例

    RLock lock1 = redissonClient1.getLock("lock");
    RLock lock2 = redissonClient2.getLock("lock");
    RLock multiLock = new RedissonMultiLock(lock1, lock2);
    multiLock.lock();
    try {// 業務邏輯
    } finally {multiLock.unlock();
    }
    

五、總結
機制實現原理
可重入鎖使用 Redis Hash 結構存儲鎖名、線程唯一標識(UUID+線程ID)和重入次數。同一線程多次獲取鎖時重入次數遞增,釋放時遞減,歸零后刪除鎖。
鎖重試通過 Pub/Sub 訂閱鎖釋放事件 避免輪詢;失敗后按退避策略(默認 1.5 秒)重試,直到超時或成功。
WatchDog后臺線程每 10 秒(默認)檢查鎖持有狀態,若鎖存在則續期(重置過期時間至 30 秒)。未指定鎖超時時間時自動啟用。
主從一致性使用 MultiLock/RedLock:向多個獨立節點加鎖,需半數以上成功;解鎖時向所有節點發送命令,解決主從異步復制導致的鎖失效。

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/diannao/83858.shtml
繁體地址,請注明出處:http://hk.pswp.cn/diannao/83858.shtml
英文地址,請注明出處:http://en.pswp.cn/diannao/83858.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

srs-7.0 支持obs推webrtc流

demo演示 官方教程: https://ossrs.net/lts/zh-cn/blog/Experience-Ultra-Low-Latency-Live-Streaming-with-OBS-WHIP 實現原理就是通過WHIP協議來傳輸 SDP信息 1、運行 ./objs/srs -c conf/rtc.conf 2、obs推流 3、web端播放webrtc流 打開web:ht

面試題——JDBC|Maven|Spring的IOC思想|DI思想|SpringMVC

目錄 一、JDBC 1、jdbc連接數據庫的基本步驟&#xff08;掌握**&#xff09; 2、Statement和PreparedStatement的區別 &#xff08;掌握***&#xff09; 二、Maven 1、maven的作用 2、maven 如何排除依賴 3、maven scope作用域有哪些&#xff1f; 三、Spring的IOC思想 …

從代碼學習數學優化算法 - 拉格朗日松弛 Python版

文章目錄 前言1. 問題定義 (Problem Definition)2. 拉格朗日松弛 (Lagrangian Relaxation)3. 拉格朗日對偶問題 (Lagrangian Dual)4. 次梯度優化 (Subgradient Optimization)5. Python 代碼實現導入庫和問題定義輔助函數:求解拉格朗日松弛子問題次梯度優化主循環結果展示與繪圖…

密碼學實驗

密碼學實驗二 一、實驗目的&#xff08;本次實驗所涉及并要求掌握的知識點&#xff09; 掌握RSA算法的基本原理并根據給出的RSA算法簡單的實現代碼源程序,以及能夠使用RSA對文件進行加密。掌握素性測試的基本原理&#xff0c;并且會使用Python進行簡單的素性測試以及初步理解…

力扣面試150題-- 從中序與后序遍歷序列構造二叉樹

Day 44 題目描述 思路 這題類似與昨天那題&#xff0c;首先來復習一下&#xff0c;后序遍歷&#xff0c;對于后序遍歷每一個元素都滿足以下規律&#xff1a; &#xff08;左子樹&#xff09;&#xff08;右子樹&#xff09;&#xff08;根&#xff09;&#xff0c;那么我們直…

2區組的2水平析因實驗的混區設計

本文是實驗設計與分析&#xff08;第6版&#xff0c;Montgomery著傅玨生譯)第7章2k析因的區組化和混區設計第7.4節的python解決方案。本文盡量避免重復書中的理論&#xff0c;著于提供python解決方案&#xff0c;并與原書的運算結果進行對比。您可以從Detail 下載實驗設計與分析…

反向傳播算法——矩陣形式遞推公式——ReLU傳遞函數

總結反向傳播算法。 來源于https://udlbook.github.io/udlbook/&#xff0c;我不明白初始不從 x 0 \boldsymbol{x}_0 x0?開始&#xff0c;而是從 z 0 \boldsymbol{z}_0 z0?開始&#xff0c;不知道怎么想的。 考慮一個深度神經網絡 g [ x i , ? ] g[\boldsymbol{x}_i, \bold…

2025年PMP 學習二十三 16章 高級項目管理

2025年PMP 學習二十三 16章 高級項目管理 文章目錄 2025年PMP 學習二十三 16章 高級項目管理高級項目管理戰略管理戰略管理的組成要素&#xff1a;企業戰略轉化為戰略行動的階段&#xff1a; 組織戰略類型戰略組織類型組織級項目管理OPM&#xff08;公司項目管理&#xff09; 組…

Journal of Real-Time Image Processing 投稿過程

投稿要求雙欄12頁以內(包括參考文獻)&#xff0c;這個排版要求感覺不是很嚴格&#xff0c;我當時就是用普通的雙欄的格式去拍的版&#xff0c;然后就提交了&#xff0c;也沒單獨去下載模版。 投稿過程 12.12 Submission received 12.12 Submission is under technical check 1…

t檢驗詳解:原理、類型與應用指南

t檢驗詳解&#xff1a;原理、類型與應用指南 t檢驗&#xff08;t-test&#xff09;是一種用于比較兩組數據均值是否存在顯著差異的統計方法&#xff0c;適用于數據近似正態分布且滿足方差齊性的場景。以下從核心原理、檢驗類型、實施步驟到實際應用進行系統解析。 一、t檢驗的…

Web4X·AI實業未來家庭普及產品矩陣

Web4XAI實業未來家庭普及產品矩陣 > 打造一個“AI能干活、人更自由”的超級生活系統&#xff08;web4-web4.0&#xff09; 一、AI生活服務類 1、代表產品&#xff1a; ? AI語音助手&#xff08;對話、提醒、天氣、家庭調度&#xff09; ? AI陪護機器人&#xff08;老…

Centos上搭建 OpenResty

一、OpenResty簡介 OpenResty 是基于 Nginx 的擴展平臺&#xff0c;完全兼容 Nginx 的核心功能&#xff08;如 HTTP 服務和反向代理&#xff09;&#xff0c;同時通過內嵌 LuaJIT 支持&#xff0c;允許開發者用 Lua 腳本靈活擴展業務邏輯。它簡化了動態邏輯的實現。 二、安裝…

項目管理進階:基于IPD流程的項目管理部分問題及建議書【附全文閱讀】

該文檔主要探討了研發項目管理中存在的問題及改進建議。指出項目組織、項目計劃、項目監控等方面存在的問題&#xff0c;并給出了相應的設計要點。建議建立跨部門、全流程的項目計劃體系&#xff0c;加強風險管理&#xff0c;引入科學的估計方法&#xff0c;建立項目歷史數據積…

JVM之GC常見的垃圾回收器

收集器適用區域特點適用場景Serial新生代單線程&#xff0c;STW&#xff08;Stop-The-World&#xff09;客戶端小應用Parallel Scavenge新生代多線程&#xff0c;吞吐量優先后臺計算任務ParNew新生代Serial 的多線程版配合 CMS 使用CMS老年代并發標記&#xff0c;低延遲響應優先…

免費私有化部署! PawSQL社區版,超越EverSQL的企業級SQL優化工具面向個人開發者開放使用了

1. 概覽 1.1 快速了解 PawSQL PawSQL是專注于數據庫性能優化的企業級工具&#xff0c;解決方案覆蓋SQL開發、測試、運維的整個流程&#xff0c;提供智能SQL審核、查詢重寫優化及自動化巡檢功能&#xff0c;支持MySQL、PostgreSQL、Oracle、SQL Server等主流數據庫及達夢、金倉…

HTTP/HTTPS與SOCKS5協議在隧道代理中的兼容性設計解析

目錄 引言 一、協議特性深度對比 1.1 協議工作模型差異 1.2 隧道代理適配難點 二、兼容性架構設計 2.1 雙協議接入層設計 2.2 統一隧道內核 三、關鍵技術實現 3.1 協議轉換引擎 3.1.1 HTTP→SOCKS5轉換 3.1.2 SOCKS5→HTTP轉換 3.2 連接管理策略 3.2.1 智能連接池 …

3DGS——基礎知識學習筆記

1.什么是3D高斯潑濺&#xff08;3D Gaussian Splatting&#xff09;&#xff1f; 目標&#xff1a;從一組稀疏的3D點&#xff08;比如通過相機或激光雷達采集的點云&#xff09;重建出高質量的3D場景&#xff0c;并支持實時渲染。 核心思想&#xff1a;用許多“3D高斯分布”&…

【C++】不推薦使用的std::allocator<void>

文章目錄 不推薦使用的std::allocator<void>1. 核心區別2. 成員函數對比(1) allocate 和 deallocate(2) construct 和 destroy 3. 設計動機(1) std::allocator<T>(2) std::allocator<void> 4. 使用場景示例(1) std::allocator<int>(2) std::allocator&…

Go 語言云原生微服務全棧實戰:Docker 鏡像優化、K8s 編排與 Istio 流量治理

本系列文章將以 Go 語言為主導開發語言&#xff0c;系統性地講解如何從零構建一個基于微服務架構的應用系統&#xff0c;涵蓋以下核心模塊&#xff1a; 使用 Go 構建高性能微服務構建精簡且高效的 Docker 鏡像利用 Kubernetes 進行微服務編排與部署通過 Istio 實現微服務的流量…

windows下authas調試tomcat

一般情況下&#xff0c;我們只需要輸入以下代碼 java -jar authas.jar調試tomcat時需要加上進程號 java -jar authas.jar <PID> 此外&#xff0c;如果你使用的是 Java 11 或更高版本&#xff0c;你需要添加 --add-opens 參數&#xff0c;以便 Arthas 能夠訪問 JVM 的內…