默認的加鎖邏輯是非公平的。程序員
在加鎖失敗時,線程會進入 while 循環,一直嘗試得到鎖,這時候是多線程進行競爭。就是說誰搶到就是誰的。面試
Redisson 提供了 公平鎖 機制,使用方式以下:redis
RLock fairLock = redisson.getFairLock("anyLock"); // 最多見的使用方法 fairLock.lock();
下面一塊兒看下公平鎖是如何實現的?數據結構
直接定位到源碼方法:RedissonFairLock#tryLockInnerAsync
。多線程
好傢伙,這一大塊代碼,我截圖也截不完,我們直接分析 lua 腳本。ide
PS:雖然咱不懂 lua,可是這一堆堆的 if else 我們大概仍是能看懂的。lua
由於 debug 發現 command == RedisCommands.EVAL_LONG
,因此直接看下面一部分。spa
這麼長,連呼好幾聲好傢伙!線程
先來看看參數都有啥?debug
anyLock
;redisson_lock_queue:{anyLock}
;redisson_lock_timeout:{anyLock}
,是按照鎖的時間戳存放到集合中的;a3da2c83-b084-425c-a70f-5d9a08b37f31:1
;
加鎖隊列和集合是含有大括號的字符串。{XXXX} 是指這個 key 僅使用 XXXX 用來計算 slot 的位置。
上面的 lua 腳本是分爲幾塊的,我們分別從不一樣的角度看下上面代碼的執行。
首次加鎖(Thread1)
第一部分,由於是首次加鎖,因此等待隊列爲空,直接 跳出循環。這一部分執行結束。
第二部分:
執行完這裏就 return
了。因此後面幾部分就暫時不看了。
至關於下面兩個命令(整個 lua 腳本都是原子的!):
> hset anyLock a3da2c83-b084-425c-a70f-5d9a08b37f31:1 1 > pexpire anyLock 30000
Thread2 加鎖
當 Thread1 加鎖完成以後,此時 Thread2 來加鎖。
Thread2 能夠是本實例其餘線程,也能夠是其餘實例的線程。
第一部分,雖然鎖被 Thread1 佔用了,可是等待隊列是空的,直接跳出循環。
第二部分,鎖存在,直接跳過。
第三部分,線程是否持鎖,沒有持鎖,直接跳過。
第四部分,線程是否在等待隊列中,Thread2 纔來加鎖,不在裏面,直接跳過。
Thread2 最後會來到這裏:
redisson_lock_queue:{anyLock}
中獲取最後一個線程;ttl anyLock
;60000*5
;zadd KEYS[3] timeout ARGV[2]
這裏使用 zadd 命令分別放置的是,redisson_lock_timeout:{anyLock}
,超時時間戳(1624612689520),線程(UUID2:Thread2)。
其中超時時間戳當分數,用來在有序集合中排序,表示加鎖的順序。
Thread3 加鎖
Thread1 佔有了鎖,Thread2 在等待,此時線程 3 來了。
獲取 firstThreadId2 此時隊列是有線程的是 UUID2:Thread2。
判斷 firstThreadId2 的分數(超時時間戳)是否是小於當前時間戳:
第2、3、四部分都不知足條件。
Thread3 最後也會來到這裏:
redisson_lock_queue:{anyLock}
中獲取最後一個線程;60000*5
,在最後一個線程的超時時間上加上 300000 以及當前時間戳,就是 Thread3 的超時時間戳。
本文主要總結了公平鎖的加鎖邏輯,這涉及到比較多的 Redis 操做,作一下簡要總結:
須要理解的就是這裏會額外添加一個等待隊列,以及有序集合。
對照着 Java 公平鎖源碼閱讀,理解起來效果更好。
最近我整理了整套《JAVA核心知識點總結》,說實話 ,做爲一名Java程序員,不論你需不須要面試都應該好好看下這份資料。拿到手老是不虧的~個人很多粉絲也所以拿到騰訊字節快手等公司的Offer
進【Java進階之路羣】,找管理員獲取哦-!