【Redis】筆記|第5節|Redisson實現高并發分布式鎖核心源碼

一、加鎖流程

1.?核心方法調用鏈
RLock lock = redisson.getLock("resource");
lock.lock(); // 阻塞式加鎖? lockInterruptibly()? tryAcquire(-1, leaseTime, unit) // leaseTime=-1表示啟用看門狗? tryAcquireAsync()? tryLockInnerAsync() // 執行Lua腳本
2.?Lua腳本實現(關鍵)
// RedissonLock.tryLockInnerAsync()
"if (redis.call('exists', KEYS[1]) == 0) then " +  // 鎖不存在"redis.call('hset', KEYS[1], ARGV[2], 1); " +  // 創建鎖(Hash結構)"redis.call('pexpire', KEYS[1], ARGV[1]); " +  // 設置過期時間"return nil; " +
"end; " +
"if (redis.call('hexists', KEYS[1], ARGV[2]) == 1) then " +  // 鎖已存在,判斷是否重入"redis.call('hincrby', KEYS[1], ARGV[2], 1); " +  // 重入次數+1"redis.call('pexpire', KEYS[1], ARGV[1]); " +  // 重置過期時間"return nil; " +
"end; " +
"return redis.call('pttl', KEYS[1]);",  // 返回剩余時間,加鎖失敗
Collections.singletonList(getName()),  // KEYS[1]: 鎖名稱(如"resource")
internalLockLeaseTime, getLockName(threadId)  // ARGV[1]: 過期時間;ARGV[2]: 線程標識(UUID:threadId)
3.?關鍵點
  • 原子性:通過Lua腳本保證檢查鎖和創建鎖的原子性。
  • 數據結構:使用Redis的Hash存儲鎖信息,field為線程標識,value為重入次數。
  • 過期時間:默認30秒(看門狗機制自動續期),防止死鎖。

二、解鎖流程

1.?核心方法調用鏈
lock.unlock();? unlockAsync()? unlockInnerAsync() // 執行Lua腳本
2.?Lua腳本實現(關鍵)
// RedissonLock.unlockInnerAsync()
"if (redis.call('hexists', KEYS[1], ARGV[3]) == 0) then " +  // 鎖不存在或非當前線程持有"return nil; " +
"end; " +
"local counter = redis.call('hincrby', KEYS[1], ARGV[3], -1); " +  // 重入次數-1
"if (counter > 0) then " +  // 重入次數>0,繼續持有鎖"redis.call('pexpire', KEYS[1], ARGV[2]); " +  // 重置過期時間"return 0; " +
"else " +  // 重入次數=0,釋放鎖"redis.call('del', KEYS[1]); " +  // 刪除鎖"redis.call('publish', KEYS[2], ARGV[1]); " +  // 發布鎖釋放消息(通知等待線程)"return 1; " +
"end; " +
"return nil;",
Arrays.asList(getName(), getChannelName()),  // KEYS[1]: 鎖名稱;KEYS[2]: 發布訂閱通道
LockPubSub.unlockMessage, internalLockLeaseTime, getLockName(threadId)  // ARGV[3]: 線程標識
3.?關鍵點
  • 安全釋放:僅鎖持有者(UUID:threadId匹配)可釋放鎖。
  • 發布訂閱:鎖釋放時通過Redis的PUBLISH通知等待線程。
  • 重入處理:通過hincrby -1遞減重入次數,確保正確釋放。

三、鎖續時(看門狗機制)

1.?觸發條件
  • 當使用無參lock()方法時(即未指定leaseTime),默認啟用看門狗。
  • 看門狗默認每10秒(internalLockLeaseTime / 3)續期一次,將鎖過期時間重置為30秒。
2.?核心源碼
// RedissonLock.scheduleExpirationRenewal()
private void scheduleExpirationRenewal(long threadId) {ExpirationEntry entry = new ExpirationEntry();ExpirationEntry oldEntry = EXPIRATION_RENEWAL_MAP.putIfAbsent(getEntryName(), entry);entry.addThreadId(threadId);// 創建定時任務Timeout task = commandExecutor.getConnectionManager().newTimeout(timeout -> {RFuture<Boolean> future = renewExpirationAsync(threadId);future.whenComplete((res, e) -> {if (e != null) {log.error("Can't update lock " + getName() + " expiration", e);return;}if (res) {// 續期成功,遞歸調用scheduleExpirationRenewal(threadId);}});}, internalLockLeaseTime / 3, TimeUnit.MILLISECONDS);entry.setTimeout(task);
}
3.?續期Lua腳本
// RedissonLock.renewExpirationAsync()
"if (redis.call('hexists', KEYS[1], ARGV[2]) == 1) then " +  // 鎖存在且為當前線程持有"redis.call('pexpire', KEYS[1], ARGV[1]); " +  // 重置過期時間"return 1; " +
"end; " +
"return 0;",
Collections.singletonList(getName()),
internalLockLeaseTime, getLockName(threadId)
4.?關鍵點
  • 自動續期:通過Netty的Timeout實現定時任務。
  • 避免死鎖:若業務執行時間超長,看門狗會持續續期,直到業務完成或線程崩潰。

四、重入鎖實現

1.?數據結構

使用Redis的Hash存儲鎖信息:

  • Key:鎖名稱(如"resource")。
  • Field:線程標識(UUID:threadId)。
  • Value:重入次數(初始為1,每次重入+1)。
2.?加鎖時的重入邏輯
// Lua腳本片段
"if (redis.call('hexists', KEYS[1], ARGV[2]) == 1) then " +  // 鎖已存在,判斷是否重入"redis.call('hincrby', KEYS[1], ARGV[2], 1); " +  // 重入次數+1"redis.call('pexpire', KEYS[1], ARGV[1]); " +  // 重置過期時間"return nil; " +  // 返回nil表示加鎖成功(重入)
"end; "
3.?解鎖時的重入邏輯
// Lua腳本片段
"local counter = redis.call('hincrby', KEYS[1], ARGV[3], -1); " +  // 重入次數-1
"if (counter > 0) then " +  // 重入次數>0,繼續持有鎖"redis.call('pexpire', KEYS[1], ARGV[2]); " +  // 重置過期時間"return 0; " +  // 返回0表示鎖未釋放
"else " +  // 重入次數=0,釋放鎖"redis.call('del', KEYS[1]); " +  // 刪除鎖"return 1; " +  // 返回1表示鎖已釋放
"end; "
4.?關鍵點
  • 線程安全:通過UUID:threadId確保同一線程可重入。
  • 原子計數:使用hincrby保證計數操作的原子性。

五、lock()與tryLock()的區別

1.?核心區別對比表

特性

lock()

tryLock()

阻塞行為

阻塞直到獲取鎖

立即返回或在指定時間內等待

超時機制

無超時,默認啟用看門狗自動續期

可自定義等待時間和鎖持有時間

異常處理

不響應中斷(拋出?InterruptedException

可響應中斷(通過重載方法)

返回值

void

boolean(是否獲取鎖成功)

看門狗默認啟用

是(無參時)

否(需顯式設置超時參數)

典型場景

必須獲取鎖才能執行的場景

可重試或放棄的場景

2.?源碼差異分析
// lock() 源碼片段
public void lock() {try {lock(-1, null, false); // leaseTime=-1表示啟用看門狗} catch (InterruptedException e) {Thread.currentThread().interrupt();}
}// tryLock() 源碼片段
public boolean tryLock(long waitTime, long leaseTime, TimeUnit unit) throws InterruptedException {// 1. 計算超時時間long time = unit.toMillis(waitTime);long current = System.currentTimeMillis();long threadId = Thread.currentThread().getId();// 2. 嘗試獲取鎖Long ttl = tryAcquire(waitTime, leaseTime, unit, threadId);if (ttl == null) {return true; // 獲取成功}// 3. 超時處理邏輯(循環嘗試或等待通知)// ...
}
3.?使用場景對比
// lock() 使用示例
RLock lock = redisson.getLock("order:123");
try {lock.lock(); // 阻塞直到獲取鎖// 執行關鍵業務邏輯
} finally {lock.unlock();
}// tryLock() 使用示例
RLock lock = redisson.getLock("inventory:apple");
try {// 嘗試在5秒內獲取鎖,持有30秒if (lock.tryLock(5, 30, TimeUnit.SECONDS)) {// 獲取鎖成功,執行操作} else {// 獲取鎖失敗,執行降級邏輯}
} catch (InterruptedException e) {Thread.currentThread().interrupt();
} finally {if (lock.isHeldByCurrentThread()) {lock.unlock();}
}

六、總結

Redisson分布式鎖的核心優勢:

  1. 原子性:通過Lua腳本確保操作的原子性。
  2. 可重入:基于Hash結構實現線程級別的重入計數。
  3. 高可用:通過看門狗機制避免鎖過期導致的數據不一致。
  4. 高性能:基于Netty的異步通信模型。
  5. 安全釋放:通過UUID:threadId確保鎖只能被持有者釋放。

最佳實踐建議

  • 優先使用?tryLock():避免長時間阻塞,提高系統吞吐量。
  • 明確鎖持有時間:根據業務場景合理設置leaseTime,避免過度依賴看門狗。
  • 異常處理:使用帶超時參數的tryLock(),并處理中斷異常。

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

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

相關文章

基于React + TypeScript構建高度可定制的QR碼生成器

前言 在現代Web應用中&#xff0c;QR碼已成為連接線上線下的重要橋梁。本文將詳細介紹如何使用React TypeScript Vite構建一個功能強大、高度可定制的QR碼生成器&#xff0c;支持背景圖片、文本疊加、HTML模塊、圓角導出等高級功能。 前往試試 項目概述 技術棧 前端框架:…

【MATLAB代碼】制導——三點法,二維平面下的例程|運動目標制導,附完整源代碼

三點法制導是一種導彈制導策略,主要用于確保導彈能夠準確追蹤并擊中移動目標。該方法通過計算導彈、目標和制導站之間的相對位置關系,實現對目標的有效制導。 本文給出MATLAB下的三點法例程,模擬平面上捕獲運動目標的情況訂閱專欄后可直接查看源代碼,粘貼到MATLAB空腳本中即…

Ubuntu22.04 安裝 IsaacSim 4.2.0

1. 從官網下載 IsaacSim 4.2.0 安裝包 https://download.isaacsim.omniverse.nvidia.com/isaac-sim-standalone%404.2.0-rc.18%2Brelease.16044.3b2ed111.gl.linux-x86_64.release.zip 2. 查閱 Workstation Installation 安裝方式 Workstation Installation — Isaac Sim Do…

開源量子模擬引擎:Quantum ESPRESSO本地部署教程,第一性原理計算輕松入門!

一、介紹 Quantum ESPRESSO 是一個用于電子結構計算和納米尺度材料建模的開源計算機代碼集成套件&#xff0c;專門用于進行第一性原理&#xff08;第一性原理&#xff09;計算&#xff0c;涵蓋了電子結構、晶體學和材料性能的模擬。 Quantum ESPRESSO GPU 版本支持GPU加速&am…

PVE 虛擬機安裝 Ubuntu Server V24 系統 —— 一步一步安裝配置基于 Ubuntu Server 的 NodeJS 服務器詳細實錄1

前言 最近在基于 NodeJS V22 寫一個全棧的項目&#xff0c;寫好了&#xff0c;當然需要配置服務器部署啦。這個過程對于熟手來說&#xff0c;還是不復雜的&#xff0c;但是對于很多新手來說&#xff0c;可能稍微有點困難。所以&#xff0c;我把整個過程全部記錄一下。 熟悉我…

【JUC】深入解析 JUC 并發編程:單例模式、懶漢模式、餓漢模式、及懶漢模式線程安全問題解析和使用 volatile 解決內存可見性問題與指令重排序問題

單例模式 單例模式確保某個類在程序中只有一個實例&#xff0c;避免多次創建實例&#xff08;禁止多次使用new&#xff09;。 要實現這一點&#xff0c;關鍵在于將類的所有構造方法聲明為private。 這樣&#xff0c;在類外部無法直接訪問構造方法&#xff0c;new操作會在編譯…

2. 庫的操作

2.1 創建數據庫 語法&#xff1a; CREATE DATABASE [IF NOT EXISTS] db_name [create_specification [, create_specification] ...] create_specification: [DEFAULT] CHARACTER SET charset_name # 字符集: 存儲編碼 [DEFAULT] COLLATE collation_name # 校驗集: 比較/選擇/讀…

道可云人工智能每日資訊|北京農業人工智能與機器人研究院揭牌

道可云人工智能&元宇宙每日簡報&#xff08;2025年6月3日&#xff09;訊&#xff0c;今日人工智能&元宇宙新鮮事有&#xff1a; 北京農業人工智能與機器人研究院揭牌 5月30日&#xff0c;北京市農業農村局、北京市海淀區人民政府、北京市農林科學院共同主辦北京農業人…

【JSON-to-Video】設置背景視頻片斷

目錄 設置bgVideo字段 1. 設置bgVideo.videoList字段 2. 設置randomPlay字段 3. 設置complete字段 4. 調用API&#xff0c;制作視頻 歡迎來到JSON轉視頻系列教程。今天要教大家如何添加背景視頻片斷&#xff0c;在視頻制作中&#xff0c;巧妙運用背景視頻&#xff0c;能為…

星閃開發之Server-Client 指令交互控制紅燈亮滅案例解析(SLE_LED詳解)

系列文章目錄 星閃開發之Server-Client 指令交互控制紅燈亮滅的全流程解析&#xff08;SLE_LED詳解&#xff09; 文章目錄 系列文章目錄前言一、項目地址二、客戶端1.SLE_LED_Client\inc\SLE_LED_Client.h2.SLE_LED_Client\src\SLE_LED_Client.c頭文件與依賴管理宏定義與全局變…

Linux shell練習題

Shell 1. 判斷~/bigdata.txt 是否存在&#xff0c;若已存在則打印出”該文件已存在“&#xff0c;如不存在&#xff0c;則輸出打印&#xff1a;”該文件不存在“ if [ -f ./bigdata.txt ];then echo "文件存在" else echo "文件不存在" fi2. 判斷~/bigd…

Linux基本指令(三)

接上之前的文章&#xff0c;咱繼續分享Linux的基本指令&#xff0c;Linux指令比較多&#xff0c;很難全部記住需要做筆記對常用的指令進行記錄&#xff0c;方便以后復習查找&#xff0c;做筆記也可以對知識理解更加深刻。 目錄 時間相關指令 date顯示 時間戳 cal指令 ?編…

WebRTC中sdp多媒體會話協議報文詳細解讀

sdp介紹 在WebRTC&#xff08;Web實時通信&#xff09;中&#xff0c;SDP&#xff08;Session Description Protocol&#xff09;是用來描述和協商多媒體會話的協議。它定義了會話的參數和媒體流的信息&#xff0c;如音視頻編碼格式、傳輸方式、網絡地址等。SDP是WebRTC中一個…

【MySQL】 約束

一、約束的定義 MySQL 約束是用于限制表中數據的規則&#xff0c;確保數據的 準確性 和 一致性 。約束可以在創建表時定義&#xff0c;也可以在表創建后通過修改表結構添加。 二、常見的約束類型 2.1 NOT NULL 非空約束 加了非空約束的列不能為 NULL 值&#xff0c;如果可以…

【.net core】【watercloud】樹形組件combotree導入及調用

源碼下載:combotree: 基于layui及zTree的樹下拉框組件 鏈接中提供了組件的基本使用方法 框架修改內容 1.文件導入&#xff08;路徑可更具自身情況自行設定&#xff09; 解壓后將文件夾放在圖示路徑下&#xff0c;修改文件夾名稱為combotree 2.設置路徑&#xff08;設置layu…

ES101系列07 | 分布式系統和分頁

本篇文章主要講解 ElasticSearch 中分布式系統的概念&#xff0c;包括節點、分片和并發控制等&#xff0c;同時還會提到分頁遍歷和深度遍歷問題的解決方案。 節點 節點是一個 ElasticSearch 示例 其本質就是一個 Java 進程一個機器上可以運行多個示例但生產環境推薦只運行一個…

CppCon 2015 學習:3D Face Tracking and Reconstruction using Modern C++

1. 3D面部追蹤和重建是什么&#xff1f; 3D面部追蹤&#xff08;3D Face Tracking&#xff09;&#xff1a; 實時檢測并追蹤人臉在三維空間中的位置和姿態&#xff08;如轉頭、點頭、表情變化等&#xff09;&#xff0c;通常基于攝像頭捕獲的視頻幀。3D面部重建&#xff08;3D…

代碼中的問題及解決方法

目錄 YOLOX1. AttributeError: VOCDetection object has no attribute cache2. ValueError: operands could not be broadcast together with shapes (8,5) (0,)3. windows遠程查看服務器的tensorboard4. AttributeError: int object has no attribute numel YOLOX 1. Attribu…

【JVM】Java類加載機制

【JVM】Java類加載機制 什么是類加載&#xff1f; 在 Java 的世界里&#xff0c;每一個類或接口在經過編譯后&#xff0c;都會生成對應的 .class 字節碼文件。 所謂類加載機制&#xff0c;就是 JVM 將這些 .class 文件中的二進制數據加載到內存中&#xff0c;并對其進行校驗…

vue的監聽屬性watch的詳解

文章目錄 1. 監聽屬性 watch2. 常規用法3. 監聽對象和route變化4. 使用場景 1. 監聽屬性 watch watch 是一個對象&#xff0c;鍵是需要觀察的表達式&#xff0c;用于觀察 Vue 實例上的一個表達式或者一個函數計算結果的變化。回調函數的參數是新值和舊值。值也可以是方法名&am…