分布式鎖的實現方式:使用 Redisson 實現分布式鎖( Spring Boot )

Redisson提供了分布式和可擴展的Java數據結構,包括分布式鎖的實現。

1. 添加依賴

pom.xml中添加Redisson依賴:

<dependency><groupId>org.redisson</groupId><artifactId>redisson-spring-boot-starter</artifactId><version>3.16.4</version> <!-- 根據需要選擇最新穩定版本 -->
</dependency>
2. 配置Redisson客戶端

創建Redisson配置類:

import org.redisson.Redisson;
import org.redisson.api.RedissonClient;
import org.redisson.config.Config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;@Configuration
public class RedissonConfig {@Beanpublic RedissonClient redissonClient() {// 創建單機模式的配置Config config = new Config();config.useSingleServer().setAddress("redis://localhost:6379").setPassword("yourPassword") // 如果有密碼.setDatabase(0);// 集群模式配置示例/*config.useClusterServers().addNodeAddress("redis://node1:7000", "redis://node2:7001").setPassword("password").setScanInterval(2000); // 集群狀態掃描間隔*/return Redisson.create(config);}
}
3. 使用Redisson分布式鎖

以下是幾種常見的鎖使用方式:

3.1 可重入鎖(Reentrant Lock)
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;import java.util.concurrent.TimeUnit;@Service
public class OrderService {@Autowiredprivate RedissonClient redissonClient;public void createOrder(String orderId) {String lockName = "order-lock:" + orderId;RLock lock = redissonClient.getLock(lockName);try {// 方式1:阻塞式獲取鎖(默認30秒自動釋放)lock.lock();// 方式2:帶超時的獲取鎖(等待100秒,自動釋放時間30秒)// boolean isLocked = lock.tryLock(100, 30, TimeUnit.SECONDS);// if (isLocked) { ... }// 執行業務邏輯System.out.println("獲取鎖成功,處理訂單: " + orderId);Thread.sleep(2000);} catch (InterruptedException e) {Thread.currentThread().interrupt();} finally {// 釋放鎖lock.unlock();}}
}
3.2 公平鎖(Fair Lock)
public void createOrderFair(String orderId) {String lockName = "fair-order-lock:" + orderId;// 獲取公平鎖RLock fairLock = redissonClient.getFairLock(lockName);try {fairLock.lock();// 執行業務邏輯} finally {fairLock.unlock();}
}
3.3 讀寫鎖(ReadWriteLock)
import org.redisson.api.RReadWriteLock;public void updateProductStock(String productId) {String lockName = "product-rw-lock:" + productId;RReadWriteLock rwLock = redissonClient.getReadWriteLock(lockName);// 寫鎖(互斥)try {rwLock.writeLock().lock();// 執行寫操作,如更新庫存} finally {rwLock.writeLock().unlock();}// 讀鎖(共享)try {rwLock.readLock().lock();// 執行讀操作,如查詢庫存} finally {rwLock.readLock().unlock();}
}
4. Redisson分布式鎖的特性
  1. 自動續約(WatchDog機制)
    當獲取鎖時未指定釋放時間,Redisson會自動延長鎖的過期時間,防止業務執行時間過長導致鎖提前釋放。

  2. 可重入性
    同一線程可以多次獲取同一把鎖而不會死鎖,需要相應次數的釋放。

  3. 異常處理
    Redisson的鎖釋放操作在finally塊中執行,確保即使發生異常也能釋放鎖。

  4. 異步和響應式支持
    支持異步獲取鎖和響應式編程模型:

    // 異步獲取鎖
    CompletableFuture<Boolean> future = lock.tryLockAsync(100, 30, TimeUnit.SECONDS);
    
5. RedLock算法實現(多節點模式)

RedLock用于在多個Redis節點上實現更高可靠性的分布式鎖:

import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.redisson.client.RedisClient;public void redLockExample() {// 創建多個Redisson客戶端連接不同節點RedissonClient client1 = Redisson.create(...);RedissonClient client2 = Redisson.create(...);RedissonClient client3 = Redisson.create(...);RLock lock1 = client1.getLock("lock1");RLock lock2 = client2.getLock("lock2");RLock lock3 = client3.getLock("lock3");// 創建RedLockorg.redisson.api.RedLock redLock = new org.redisson.api.RedLock(lock1, lock2, lock3);try {// 嘗試獲取鎖(多數節點成功才算成功)boolean isLocked = redLock.tryLock(100, 30, TimeUnit.SECONDS);if (isLocked) {// 執行業務邏輯}} finally {redLock.unlock();}
}

6.可重入鎖示例

6.1示例背景及需求

在很多實際業務場景中,我們執行的操作可能由于各種臨時原因(比如網絡抖動、資源暫時不可用等)而失敗,此時往往希望能夠進行重試,以增加操作最終成功的概率。同時,為了保證在重試過程中對共享資源的訪問是互斥的(避免多個線程同時操作造成數據不一致等問題),就需要使用可重入鎖來進行控制。

6.2詳細代碼示例
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;import java.util.concurrent.TimeUnit;@Service
public class RetryableOperationService {@Autowiredprivate RedissonClient redissonClient;// 定義最大重試次數private static final int MAX_ATTEMPTS = 3;// 定義重試間隔時間(單位:毫秒)private static final long RETRY_INTERVAL = 100;public void retryableOperation(String key) {String lockName = "retry-lock:" + key;RLock lock = redissonClient.getLock(lockName);lock.lock();try {int attempts = 0;while (attempts < MAX_ATTEMPTS) {try {// 這里是具體要執行的可能失敗的業務操作,比如調用外部接口獲取數據、更新數據庫等System.out.println("正在執行第 " + (attempts + 1) + " 次嘗試操作...");// 模擬業務操作可能出現的異常情況,這里簡單地通過拋異常來表示操作失敗if (attempts == 1) {throw new RuntimeException("模擬操作失敗");}// 如果操作成功,直接返回,不再進行重試return;} catch (Exception e) {attempts++;System.out.println("操作失敗,第 " + attempts + " 次重試,等待 " + RETRY_INTERVAL + " 毫秒后再次嘗試...");// 線程休眠一段時間,等待后再次嘗試Thread.sleep(RETRY_INTERVAL);}}System.out.println("達到最大重試次數,操作最終失敗");} catch (InterruptedException ex) {Thread.currentThread().interrupt();System.out.println("線程被中斷,操作終止");} finally {// 無論操作最終是否成功,都要釋放鎖,確保其他線程可以獲取鎖來執行操作lock.unlock();}}
}
6.3代碼執行流程說明
  1. 獲取鎖
    首先,根據傳入的 key 生成對應的鎖名稱(retry-lock:具體的key值),然后通過 RedissonClient 獲取可重入鎖 lock,并調用 lock() 方法獲取鎖,這一步保證了在同一時刻只有一個線程能夠執行后續的重試操作,避免并發沖突。

  2. 執行操作與重試邏輯
    進入 while 循環,開始嘗試執行具體的業務操作。在每次循環中:

    • 嘗試執行業務操作:這里只是簡單地模擬了業務操作,通過拋異常來表示操作失敗(實際應用中可能是調用外部接口返回錯誤碼、數據庫更新失敗等情況)。如果操作成功(沒有拋出異常),則直接通過 return 語句結束方法,不再進行重試。
    • 處理操作失敗情況:當操作拋出異常時,表示此次嘗試失敗。attempts 變量記錄當前的重試次數,每次失敗后將其加1,并輸出相應的重試提示信息,然后讓線程休眠 RETRY_INTERVAL(100毫秒)時間,等待一段時間后再次進行下一次嘗試。
  3. 重試次數限制與最終結果處理
    while 循環的條件是 attempts < MAX_ATTEMPTS,也就是最多只會重試 MAX_ATTEMPTS(定義為3次)次。當達到最大重試次數后,如果操作還是沒有成功,就會輸出操作最終失敗的提示信息。

  4. 釋放鎖
    無論操作最終是成功還是達到最大重試次數失敗,都會進入 finally 塊釋放鎖。這一步非常關鍵,確保了鎖資源能夠被正確釋放,使得其他等待獲取該鎖的線程有機會執行相應的操作。

6.4適用場景舉例
  1. 網絡請求重試
    比如在一個微服務架構中,服務A需要調用服務B的接口獲取數據來更新本地數據庫。由于網絡環境不穩定,可能出現調用失敗的情況。此時,服務A就可以使用上述帶有可重入鎖的重試機制,在獲取到鎖后,多次嘗試調用服務B的接口,保證在重試過程中不會有其他線程同時進行相同的調用操作,避免數據不一致等問題。

  2. 數據庫更新重試
    當向數據庫插入或更新一條重要記錄時,可能因為數據庫臨時負載過高、事務沖突等原因導致操作失敗。通過使用這種帶可重入鎖的重試機制,可以在一定次數內反復嘗試更新操作,同時保證在整個重試期間,其他線程不會干擾該記錄的更新,確保數據的完整性和一致性。

6.5總結

可重入鎖主要用于以下方面:

  1. 確保操作的互斥性
    在整個 retryableOperation 方法執行期間,從獲取鎖開始到最終釋放鎖,只有獲取到鎖的那個線程能夠執行后續的業務操作以及重試邏輯。例如,假設有多個線程同時調用 retryableOperation 方法并傳入相同的 key,由于鎖的存在,只有最先成功獲取到鎖(鎖名為 retry-lock:具體的key值)的線程能夠進入到方法內部開始執行可能失敗的業務操作以及重試流程,其他線程會被阻塞等待鎖釋放,從而保證了針對同一 key 對應的操作在同一時刻是互斥執行的,避免了多個線程同時操作造成的數據不一致、資源競爭等問題。
  2. 支持重試過程中的再次進入
    因為是可重入鎖,在重試邏輯中(while 循環內),當業務操作失敗后線程進行休眠,然后再次嘗試執行業務操作時,同一線程能夠再次獲取到已經持有的這把鎖(無需等待鎖釋放后重新競爭獲取),可以順利進入循環體繼續執行后續的操作,不會出現因為鎖的限制而導致自己阻塞自己的情況。如果使用的是不可重入鎖,那么在重試時線程再次嘗試獲取鎖就會被阻塞,因為它自己已經持有這把鎖了,這樣就無法完成重試邏輯,會陷入死鎖狀態。

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

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

相關文章

Web基礎關鍵_004_CSS(二)

目 錄 一、樣式 1.行內樣式 2.內部樣式 3.外部樣式 二、選擇器優先級 1.非關系選擇器 2.關系選擇器 三、屬性 四、盒子模型 五、元素 1.塊級元素 2.行內元素 3.行內塊級元素 4.元素類型轉換 六、浮動 七、定位 1.靜態定位 2.相對定位 3.絕對定位 4.固定定位 …

數據使用權與所有權分離:能否誕生“數據租賃”市場

——首席數據官高鵬律師數字經濟團隊創作&#xff0c;AI輔助 數據如礦藏&#xff0c;開采需“契約” 想象一座蘊藏著無盡資源的數字礦山&#xff1a;企業或個人擁有數據的“所有權”&#xff0c;如同手握礦脈的產權&#xff0c;但若無法高效挖掘其價值&#xff0c;礦石終將沉…

【esp32s3】2 - 第一個組件

下面的內容編寫時間跨度有點大&#xff0c;亂了得一團&#xff0c;也沒ai整理。食之無味&#xff0c;棄之可惜。 推薦筆記&#xff1a;ESP32 之 ESP-IDF 教學&#xff08;十八&#xff09;—— 組件配置&#xff08;KConfig&#xff09; 推薦筆記&#xff1a;Kconfig 拓展 樂鑫…

【Java_EE】單例模式、阻塞隊列、線程池、定時器

目錄 單例模式 餓漢模式<代碼> 懶漢模式<代碼> 阻塞隊列 阻塞隊列概念 阻塞隊列解決的問題 阻塞隊列工作原理 阻塞隊列的優/缺點 優點 缺點 模擬實現阻塞隊列<代碼> 線程池 線程池概念 線程池解決的問題 線程池參數 四種拒絕策略 線程池工作…

Redis初識第七期---ZSet的命令和應用場景

ZSet相較于Set來說&#xff0c;它又是有序的&#xff0c;這個有序指的就是我們通常意義上的有序了&#xff0c;ZSet內部中是按照升序來排序的。 排序規則&#xff1a;ZSet相較于Set來說&#xff0c;它內部引入了一個新的屬性&#xff1a;分數&#xff08;Score&#xff09;&am…

Wps開放平臺v5升級v7上傳實體文件踩坑(Java使用restTemplate)

背景&#xff1a; 最近接到一個老項目需求&#xff0c;之前開發的WPS開放平臺文件&#xff08;商密集成&#xff09;預覽功能因為升級需要重新對接api&#xff0c;新的上傳文件接口踩坑特意記錄一下。 這里出問題的是第二步&#xff0c;請求文件上傳信息 踩坑代碼 調用后403 p…

啥時候上RAG?啥時候上微調?丨實戰筆記

哈嘍&#xff0c;大家好&#x1f44f; 我是阿星&#xff01; 現在很多AI科普文章都會提到微調&#xff0c;RAG。 但是沒有實戰的過的同學可能會問&#x1f914;—— 啥時候用RAG&#xff1f;啥時候用微調呢&#xff1f;有啥區別&#xff1f;不都是讓模型增加知識面的嗎&…

RabbitMQ-基礎篇

前言&#xff1a; 今天開始學RabbitMQ,還是跟著黑馬的課程。 今日所學&#xff1a; RabbitMQ介紹RabbitMQ入門Java客戶端中的MQ 1.RabbitMQ介紹 1.1 什么是RabbitMQ RabbitMQ 是一個開源的消息代理軟件&#xff08;消息隊列中間件&#xff09;&#xff0c;實現了高級消息…

docker-compose配置redis哨兵詳細步驟和配置文件

docker-compose配置redis哨兵詳細步驟和配置文件 目錄結構調整 redis-cluster/ ├── config/ │ ├── master.conf # 主節點配置 │ ├── slave1.conf # 從節點1配置 │ ├── slave2.conf # 從節點2配置 │ ├── sentinel1.…

多模態大語言模型arxiv論文略讀(146)

Exploring Response Uncertainty in MLLMs: An Empirical Evaluation under Misleading Scenarios ?? 論文標題&#xff1a;Exploring Response Uncertainty in MLLMs: An Empirical Evaluation under Misleading Scenarios ?? 論文作者&#xff1a;Yunkai Dang, Mengxi G…

【教程】Linux中限制用戶可以使用的GPU數量 | 附腳本

轉載請注明出處&#xff1a;小鋒學長生活大爆炸[xfxuezhagn.cn] 如果本文幫助到了你&#xff0c;歡迎[點贊、收藏、關注]哦~ 目錄 背景說明 設置方法 管理腳本 進階限制 恢復默認組 注意事項 背景說明 比較簡單的方式是使用group來管理權限&#xff0c;這種方式能限制哪些…

90.xilinx復位低電平(一般使用低電平復位)

Xilinx FPGA 中的寄存器&#xff08;Flip-Flop&#xff09;**確實支持異步復位**&#xff0c;但具體實現方式取決于你使用的設計方法&#xff08;HDL 代碼風格或原語實例化&#xff09;。以下是詳細說明&#xff1a; --- ### 1. **Xilinx 寄存器的復位特性** - **同步復位…

NVMe高速傳輸之擺脫XDMA設計10: DMA 控制單元設計

DMA 控制單元負責控制 DMA 傳輸事務&#xff0c; 該單元承擔了 DMA 事務到 NVMe 事務的轉換任務&#xff0c; 使用戶對數據傳輸事務的控制更加簡單快捷。 DMA 控制功能由 DMA寄存器組實現。 DMA 寄存器組包含 DMA 操作寄存器、 DMA 長度寄存器、 DMA 源目的地址寄存器和 DMA 狀…

如何設置電腦定時休眠?操作指南詳解

長時間運行電腦會導致硬件過熱&#xff0c;縮短其使用壽命。定時關機有助于讓硬件得到休息&#xff0c;降低因長時間高負荷工作導致損壞的風險。 它的界面簡潔直觀&#xff0c;功能卻十分實用&#xff0c;涵蓋了定時關機、重啟、注銷、休眠、待機以及鎖定等多種操作。 以設置“…

LeetCode[617]合并二叉樹

思路&#xff1a; 我們合并左右子樹&#xff0c;在遞歸左右子樹的時候&#xff0c;一定要保證左右子樹不為空&#xff0c;如果左子樹為空&#xff0c;那么直接返回右子樹就行了&#xff0c;即使右子樹為空。如果右子樹為空那么直接返回左子樹就行了&#xff0c;這樣判斷完就正常…

Redis 常用五大數據類型

1、Redis 關鍵字&#xff08;Key&#xff09; keys * 查看當前庫所有keyexists [key] 判斷某個key是否存在type [key] 查看當前key的數據類型del [key] 刪除指定的key數據unlink [key] 根據value選擇非阻塞刪除&#xff0c;僅將keys從keyspace元數據中刪除&#xff0c;真正的刪…

大語言模型(LLM)專業術語匯總

1. 訓練與部署 1.1 預訓練 專業&#xff1a;在海量無標注文本&#xff08;如Common Crawl、Wikipedia&#xff09;上通過自監督學習訓練基礎語言模型&#xff0c;學習通用語言表征&#xff08;如GPT-3訓練數據達45TB&#xff09;。通俗&#xff1a;AI的“通識教育階段”&…

【Java Swing 圖形界面編程】JList 列表組件 ① ( JList 組件簡介 | 核心作用 | 關鍵特性 | 基礎用法示例 )

文章目錄 一、JList 組件簡介1、JList 概念簡介2、JList 核心作用3、JList 關鍵特性 二、JList 組件基礎用法示例1、使用 String 數組構建列表項2、使用 Vector 集合構建列表項3、使用 DefaultListModel 構建列表項 一、JList 組件簡介 1、JList 概念簡介 基本概念 : JList 組件…

【小技巧】Python+PyCharm IDE 配置解釋器出錯,環境配置不完整或不兼容。(小智AI、MCP、聚合數據、實時新聞查詢、NBA賽事查詢)

報錯信息如下&#xff1a; [unsupported] Python 3.1 (mcp-calculator-main) (3) C:\Users(xsshu\AppData\Local\Programs\Python\Python313\python.exe [unsupported] Python 3.1 C:\Users\xsshu\AppData\Local\Programs\Python\Python311\python.exe 這條輸出顯示了兩個 Pyth…

Ragflow 前后端登錄邏輯

前端登錄邏輯 路由配置&#xff1a; /login 路由指向 /pages/login 組件。登錄表單使用 Ant Design 的 Form, Input, 和 Button 組件。 登錄表單處理&#xff1a; 使用 useLogin鉤子來處理登錄請求。密碼通過 RSA 加密后再發送到服務器。成功登錄后導航至 /knowledge 頁面。 …