HashedWheelTimer
Timer
接口的實作,通過時間輪算法實作了一個定時器。
職能
根據目前時間輪指針標明對應
HashedWheelBucket
槽,從連結清單頭部開始疊代,計算每個
HashedWheelTimeout
定時任務:
- 屬于目前時鐘周期則取出運作
- 不屬于則将其剩餘的時鐘周期數減一
核心域
workerState
時間輪目前所處狀态,三個可選值,由
AtomicIntegerFieldUpdater
實作其原子地修改。

startTime
目前時間輪的啟動時間,送出到該時間輪的定時任務的 deadline 字段值均以該時間戳為起點進行計算。
wheel
時間輪環形隊列,每個元素都是一個槽。當指定時間輪槽數為 n 時,會向上取最靠近 n 的 2 次幂值
timeouts、cancelledTimeouts
HashedWheelTimer 會在處理 HashedWheelBucket 的雙向連結清單前,先處理這倆隊列的資料:
timeouts 隊列
緩沖外部送出時間輪中的定時任務
cancelledTimeouts 隊列
暫存取消的定時任務
tick
位于
HashedWheelTimer$Worker
,時間輪的指針,步長為 1 的單調遞增計數器
mask
掩碼,
mask = wheel.length - 1
,執行
ticks & mask
便能定位到對應的時鐘槽
ticksDuration
時間指針每次加 1 所代表的實際時間,機關為納秒。
pendingTimeouts
目前時間輪剩餘的定時任務總數。
workerThread
時間輪内部真正執行定時任務的線程。
worker
真正執行定時任務的邏輯封裝這個 Runnable 對象中。
newTimeout()
送出定時任務,在定時任務進入到 timeouts 隊列之前會先調用 start() 方法啟動時間輪,其中會完成下面兩個關鍵步驟:
- 确定時間輪的 startTime 字段
- 啟動 workerThread 線程,開始執行 worker 任務。
之後根據 startTime 計算該定時任務的 deadline,最後才能将定時任務封裝成
HashedWheelTimeout
并添加到
timeouts
隊列。
4 時間輪指針一次轉動的執行流程
HashedWheelTimer$Worker.run()
:
- 時間輪指針轉動,時間輪周期開始
- 清理使用者主動取消的定時任務,這些定時任務在使用者取消時,記錄到 cancelledTimeouts 隊列中。在每次指針轉動的時候,時間輪都會清理該隊列
- 将緩存在 timeouts 隊列中的定時任務轉移到時間輪中對應的槽中
- 根據目前指針定位對應槽,處理該槽位的雙向連結清單中的定時任務
- 檢測時間輪的狀态。如果時間輪處于運作狀态,則循環執行上述步驟,不斷執行定時任務。如果時間輪處于停止狀态,則執行下面的步驟擷取到未被執行的定時任務并加入
隊列:周遊時間輪中每個槽位,并調用unprocessedTimeouts
() 方法;對timeouts 隊列中未被加入槽中循環調用 poll()clearTimeouts
- 最後再次清理 cancelledTimeouts 隊列中使用者主動取消的定時任務。
5 定時任務應用
并不直接用于周期性操作,而是隻向時間輪送出執行單次的定時任務,在上一次任務執行完成的時候,調用 newTimeout() 方法再次送出目前任務,這樣就會在下個周期執行該任務。即使在任務執行過程中出現了 GC、I/O 阻塞等情況,導緻任務延遲或卡住,也不會有同樣的任務源源不斷地送出進來,導緻任務堆積。
Dubbo 時間輪應用主要在如下方面:
失敗重試, 例如,Provider 向注冊中心進行注冊失敗時的重試操作,或是 Consumer 向注冊中心訂閱時的失敗重試等
周期性定時任務, 例如,定期發送心跳請求,請求逾時的處理,或是網絡連接配接斷開後的重連機制
參考
https://zhuanlan.zhihu.com/p/32906730