1. 程式人生 > 其它 >Redisson 分散式鎖原始碼 06:公平鎖排隊加鎖

Redisson 分散式鎖原始碼 06:公平鎖排隊加鎖

前言

在上一篇文章中已經分析過公平鎖的加鎖原始碼,並得出結論:

  1. Redis Hash 資料結構:存放當前鎖,Redis Key 就是鎖,Hash 的 field 是加鎖執行緒,Hash 的 value 是 重入次數;
  2. Redis List 資料結構:充當執行緒等待佇列,新的等待執行緒會使用 rpush 命令放在佇列右邊;
  3. Redis sorted set 有序集合資料結構:存放等待執行緒的順序,分數 score 用來是等待執行緒的超時時間戳。

現在看一下加鎖失敗被放到等待佇列之後,執行緒是如何處理的?

排隊等鎖

原始碼入口:org.redisson.RedissonLock#lock(long, java.util.concurrent.TimeUnit, boolean)

執行緒進入排隊之後,在 Java 程式碼中會 while (true) 一直迴圈呼叫 tryAcquire,嘗試獲取鎖。

最終還是來到 RedissonFairLock#tryLockInnerAsync 方法中。

方便起見,重新貼一下 Lua 指令碼,以及指令碼的引數含義。

  1. KEYS[1]:加鎖的名字,anyLock
  2. KEYS[2]:加鎖等待佇列,redisson_lock_queue:{anyLock}
  3. KEYS[3]:等待佇列中執行緒鎖時間的 set 集合,redisson_lock_timeout:{anyLock},是按照鎖的時間戳存放到集合中的;
  4. ARGV[1]:鎖超時時間 30000
  5. ARGV[2]:UUID:ThreadId 組合 a3da2c83-b084-425c-a70f-5d9a08b37f31:1
  6. ARGV[3]:threadWaitTime 預設 300000
  7. ARGV[4]:currentTime 當前時間戳

原始碼分析

第一部分,while 迴圈:

  1. 從等待佇列 redisson_lock_queue:{anyLock} 中獲取第一個等待執行緒;
  2. 從等待執行緒超時集合 redisson_lock_timeout:{anyLock} 中獲取第一個等待執行緒的分數;
  3. 沒有超時,直接結束,超時了,則直接移除。

第二部分,當前鎖存在,直接跳過。

第三部分,當前鎖不是持鎖執行緒,直接跳過。

第四部分,

直接返回當前鎖還有多久到期。

當前 Redisson 版本為 3.15.6,不同版本的略有不同。

佇列重排

這裡不存在重新排序,因為官方認為這是一個 bug,重新進行了修復。

具體可以閱讀:Justin Corpron 2019/5/10, 04:13 Fix timeout drift in RedissonFairLock

最大的變化就是增加了第四部分。

圖僅僅代表兩個版本的差別,並不是代表這個版本才修改。

總結

當執行緒獲取鎖失敗,進入到等待佇列時,ttl != null,在 Java 程式碼中會不斷嘗試獲取鎖。

當鎖不存在且當前執行緒是在等待佇列頭時,直接獲得鎖。這個排隊的過程就是公平鎖的提現。

相關推薦

作者: 劉志航

公眾號:『 程式設計師小航 』

個人小站:https://liuzhihang.com/

版權宣告:本部落格所有文章除特別宣告外,均採用 CC BY-NC-SA 4.0 許可協議。轉載請註明來自 Notes