Redisson的CountDownLatch實現對比 VS Java的CountDownLat

JDK的CountDownLatch的設計思路
和鎖類似,Java實現CountDownLatch的時候也利用了一個叫做AQS的東西,源碼參照java.util.concurrent.locks.AbstractQueuedSynchronizer。

這個類的其中一個思想核心是內部的一個state狀態變量,這個狀態會告訴你當前的鎖是否可用,當前的latch是否倒計時結束等等。

核心接口連個方法,一個是countDown,另一個是await,countDown是改變狀態的,await可以類比對鎖的等待。

先看countDown
Redisson的CountDownLatch實現對比 VS Java的CountDownLat
Redisson的CountDownLatch實現對比 VS Java的CountDownLat
這裏使用自旋的方式,原子性改變狀態,如果改變成功就返回true。注意這個latch是不可重複使用的,所以如果狀態是0的話就無法再使用了。而doReleaseShared是爲了喚醒所有await該latch的線程,具體的說明可以參照https://blog.csdn.net/xxcupid/article/details/51909921。

Redisson版本的CountDownLatch設計思路
和Redisson實現分佈式鎖類似,需要利用到Redis的訂閱/發佈來實現通知的操作,來喚醒所有await當前latch的所有線程。

這裏有一個叫做ReclosableLatch的類繼承了AQS,

Redisson的CountDownLatch實現對比 VS Java的CountDownLat
看下和JDK的CountDownLatch的區別:
Redisson的CountDownLatch實現對比 VS Java的CountDownLat
Redisson在release的時候並沒有採用自旋+原子操作修改狀態,這是爲什麼呢?因爲真正控制count down的操作是redis遠端實現的,所以這裏的latch類似一個信號量的概念,只不過該信號量通知的所有等待的線程,而不是像fair鎖那樣通知一個線程,所以這裏不需要原子操作,當Redis發現當前count down計數到0,會發佈一個消息,
Redisson的CountDownLatch實現對比 VS Java的CountDownLat
這個消息會被CountDownLatchPubSub訂閱到(這裏是分佈式的消息,所以多個虛擬機都可以訂閱到這個消息,實現了分佈式的CountDownLatch功能),然後調用open方法,喚醒所有在await的線程,
Redisson的CountDownLatch實現對比 VS Java的CountDownLat
這裏有兩個狀態,OPEN和CLOSED兩個狀態,OPEN對應的是普通CountDownLatch的計數爲0的狀態,即能獲取到鎖的狀態。Redisson的初始狀態是CLOSED的狀態,所以await的線程都會被hang住。

可以通過trySetCount設置初始count值,同時發佈close消息,latch準備就緒。

Redisson的CountDownLatch方法分析
await方法:
Redisson的CountDownLatch實現對比 VS Java的CountDownLat
這個方法比較好理解,先進行訂閱操作,然後輪詢遍歷等待該latch處於open狀態,這裏會利用ReclosableLatch阻塞,當Redis發佈消息的時候,無論是open還是close,都會執行release操作喚醒該await的阻塞, 然後繼續檢驗getCount的值,如果處於OPEN狀態了(等於0),那麼說明阻塞解除,同時移除對該消息的訂閱。

帶超時時間的await

Redisson的CountDownLatch實現對比 VS Java的CountDownLat
和不帶超時時間的方法相比,就是要對時間進行校驗,如果超過過期時間了,那麼就返回失敗。

這裏的第一個if沒太看懂,執行的是CommandAsyncExecutor的await操作,這裏失敗除了超時之外還有別的原因嗎?因爲後面會檢查失敗之後,是否還要繼續等待,那就是說明還有一種情況,即使失敗了,也沒有超時?

如果第一個await失敗了,但是還沒有到超時時間,那麼繼續和上面方法一樣的邏輯,用ReclosableLatch阻塞,只不過這個阻塞是帶超時時間的阻塞,如果超時了就返回false,否則繼續輪訓校驗。

countDownAsync方法
Redisson的CountDownLatch實現對比 VS Java的CountDownLat
這裏是一段lua腳本,邏輯如下:

首先對指定key執行decr操作(在key不存在的時候,會先初始化該key值爲0,然後減一,即-1),如果v不是正數,那麼刪除該key,如果v是0,那麼在刪除key操作之後,還要發佈一個zero的消息,將該CountDownLatch設置爲OPEN狀態。

所以當key不能存在的時候,相當於什麼都沒做,因爲對key初始化並減一操作之後又將該key擦除掉了,只有當Redis中存有該key,即該latch被初始化後,纔有作用。

這裏和JDK邏輯有個不同之處,JDK的CountDownLatch在做count down的時候,是原子操作該state,但是這裏不需要,因爲lua腳本本身保證了原子性,當前

trySetCountAsync方法
Redisson的CountDownLatch實現對比 VS Java的CountDownLat
如果當前key不存在,那麼就初始化該key的次數,然後發佈newCount消息,這個消息會將本地的latch狀態設置爲CLOSE。如果key存在,那麼不做操作,即在latch初始化生效之後,不允許覆蓋當前已存在的latch的count值。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章