日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程语言 > java >内容正文

java

Redisson(2-2)分布式锁实现对比 VS Java的ReentrantLock之带超时时间的tryLock

發布時間:2024/3/13 java 39 豆豆
生活随笔 收集整理的這篇文章主要介紹了 Redisson(2-2)分布式锁实现对比 VS Java的ReentrantLock之带超时时间的tryLock 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

Redisson實現了一整套JDK中ReentrantLock的功能,這里對比一下實現的差異和核心的思想。

?

unfair模式的帶超時時間的tryLock(超時時間)

ReentrantLock

這里上來會直接先試下能不能try成功,如果不成功,進入等待并開始競爭等邏輯。

整個鎖的核心是通過LockSupport的park方法來實現的,這是調用底層UNSAFE的park方法來實現的。如果在被等待獲取的鎖釋放的時候,該線程會重新被喚醒,然后和其它線程一起競爭,如果沒有競爭成功,那么繼續park,循環往復,直到獲取到鎖或者等待超時(這里park的時間是當前要獲取鎖的線程等待獲取鎖的剩余時間,當前線程要么因為鎖釋放被喚醒,要么等到超時)。

這里有個小細節是,如果剩余的等待時間小于spinForTimeoutThreshold這個值,那么就不會再park然后等待喚醒了。這是為了防止park然后喚醒這種線程調度操作,因為剩下的等待時間已經不多,即使一直自旋也不會消耗太多的CPU性能,反而比頻繁掛起釋放要節省性能,這是一個折衷處理。

RedissonLock

RedissonLock沒有LockSupport這種底層級別的工具來幫忙,因為它是分布式的,所以它也需要借助這樣一個東西,來實現類似的功能。

我們一般在自己用Redis實現分布式鎖的時候,經常設計的操作是輪詢Redis去查詢該鎖的狀態,輪詢之間會設置一個休眠時間,休眠時間越短,當鎖釋放的時候響應的就越及時,但是對Redis的壓力就越大,因為你單位時間內輪詢Redis的次數會增加,所以這是一個痛點。那么如果我們用通知來代替輪詢,是不是就可以仿照ReentrantLock那樣,通過喚醒操作(借助通知)來喚醒本地的sleep操作,這樣就不必定時輪詢檢查狀態了。

而這個功能就要利用Redis的訂閱功能,下面看代碼:

這里和ReentrantLock一樣,先try一下,如果獲得了鎖就返回,如果沒有獲得,看下是否已經超過等待獲取鎖設置的超時時間,如果已經超時,獲取鎖失敗,否則,就要進入等待競爭的過程。

競爭的過程中,第一個操作是subcribe,即上面我們提到的訂閱功能。訂閱的超時時間即當前要獲取鎖的線程的剩余等待時間,如果在這個時間范圍內沒有響應,訂閱失敗。

訂閱的相關操作可以參考:https://my.oschina.net/u/2313177/blog/1925237。

“訂閱監聽Redis消息,并且創建RedissonLockEntry,其中RedissonLockEntry中比較關鍵的是一個Semaphore屬性對象,用來控制本地的鎖請求的信號量同步,返回的是netty框架的Future實現”。

如果這個await成功了,代表在等待鎖的超時時間之內鎖就被釋放了,接下來要進入競態部分了。

這里我理解有兩層維度,一層是應用維度,不同的虛擬機之間通過訂閱方式競爭鎖,某一臺業務服務器的java虛擬機會最終成功訂閱到這個鎖。二層是虛擬機內的線程維度,這里機器內的競爭通過一個共享鎖來減少對Redis的壓力,因為當前虛擬機的訂閱下面掛著多個競爭該鎖的不同線程,這些線程中也只有一個會成功獲得共享鎖,繼而再去競爭真正的鎖。沒有獲得共享鎖的線程睡眠一段時間,或者被喚醒繼續獲取鎖,或者超時,獲取鎖失敗,而只有獲得共享鎖成功的線程才有機會和其它虛擬機中同樣獲取共享鎖的線程共同競爭真正的鎖。

所以同虛擬機內要競爭真正鎖的所有線程先競爭一個共享鎖(Semaphore),成功的線程才獲取機會和其它虛擬機內同樣獲取共享鎖(Semaphore)的線程競爭真正的鎖——跨虛擬機的鎖競爭,即分布式鎖競爭,這里的關鍵是共享鎖降低了靜態,同時又利用訂閱機制(通知機制)解決了睡眠時間無法合理設置的問題。

其實redisson實現分布式鎖原理 - min.jiang - 博客園文章也說到了這個問題:

所以,如果訂閱成功,說明當前機器在虛擬機層面首先搶占到了資源,然后再在當前虛擬機內進行不同線程間的競爭。

像上面說的那樣,這里借助信號量來替代了ReentrantLock中的LockSupport的park功能,“通過信號量(共享鎖)阻塞,等待解鎖消息(這一點設計的非常精妙:減少了其他分布式節點的等待或者空轉等無效鎖申請的操作,整體提高了性能)。如果剩余時間(ttl)小于wait time ,就在 ttl 時間內,從Entry的信號量獲取一個許可(除非被中斷或者一直沒有可用的許可)。否則就在wait time時間范圍內等待”。

這里的共享鎖和上面subscribe的是同一把鎖。如果本虛擬機的某個線程獲取鎖之后,當它釋放鎖的時候,會發布一條取消訂閱的消息(這個后面具體會說),這樣其它虛擬機才能有平等的機會再次和本虛擬機的其他線程競爭鎖,而不是一直在本虛擬機的范圍內進行競爭,那樣其他虛擬機就會一直處于饑餓狀態。

那么什么時候這個訂閱的消息會解除當前線程的休眠操作呢?和subscribe對應的當然就是publish了,當執獲得鎖的線程進行解鎖操作的時候(解鎖后面會單獨說),在lua中會執行‘publish’操作,而publish的消息類型是LockPubSub.unlockMessage,這個消息會觸發訂閱消息的共享鎖(Semaphore)的喚醒操作,然后發起對該鎖的新一輪競爭。

而最后無論獲取鎖成功與否,都要解除當前線程對該鎖的訂閱消息。

關于等待時間(Semaphore的休眠時間),上面ReentrantLock的park的時間是當前線程獲取鎖的等待剩余時間。這里本地等待鎖的睡眠時間略有不同,使用的是Redis中鎖的生命周期剩余時間,當然在這個睡眠過程中,可能會因為鎖釋放而喚醒,因為有對當前鎖的訂閱操作。

總結

以上是生活随笔為你收集整理的Redisson(2-2)分布式锁实现对比 VS Java的ReentrantLock之带超时时间的tryLock的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。