Kafka、Dubbo、ZooKeeper、Netty、Caffeine、Akka 中都有對時間輪的實現。
時間輪簡單來説就是一個環形的隊列(底層一般基於數組實現),隊列中的每一個元素(時間格)都可以存放一個定時任務列表。
時間輪中的每個時間格代表了時間輪的基本時間跨度或者説時間精度,假如時間一秒走一個時間格的話,那麼這個時間輪的最高精度就是 1 秒(也就是説 3 s 和 3.9s 會在同一個時間格中)。
下圖是一個有 12 個時間格的時間輪,轉完一圈需要 12 s。當我們需要新建一個 3s 後執行的定時任務,只需要將定時任務放在下標為 3 的時間格中即可。當我們需要新建一個 9s 後執行的定時任務,只需要將定時任務放在下標為 9 的時間格中即可。
那當我們需要創建一個 13s 後執行的定時任務怎麼辦呢?這個時候可以引入一叫做 圈數/輪數 的概念,也就是説這個任務還是放在下標為 1 的時間格中, 不過它的圈數為 2 。
除了增加圈數這種方法之外,還有一種 多層次時間輪 (類似手錶),Kafka 採用的就是這種方案。
上圖的時間輪,第 1 層的時間精度為 1 ,第 2 層的時間精度為 20 ,第 3 層的時間精度為 400。假如我們需要添加一個 350s 後執行的任務 A 的話(當前時間是 0s),這個任務會被放在第 2 層(因為第二層的時間跨度為 20*20=400>350)的第 350/20=17 個時間格子。
當第一層轉了 17 圈之後,時間過去了 340s ,第 2 層的指針此時來到第 17 個時間格子。此時,第 2 層第 17 個格子的任務會被移動到第 1 層。
任務 A 當前是 10s 之後執行,因此它會被移動到第 1 層的第 10 個時間格子。
時間輪比較適合任務數量比較多的定時任務場景,它的任務寫入和執行的時間複雜度都是 0(1)。
時間輪算法具有以下優點:
- 高效性:時間輪算法能夠快速地查找和執行定時任務,特別適用於大量定時任務的情況。
- 公平性:時間輪算法對所有定時任務都是公平的,每個定時任務都有機會在時間輪上獲得一個槽位。
- 擴展性:時間輪算法可以容易地擴展到多處理器系統中,只需要將時間輪上的槽位擴展到多個處理器上即可。
然而,時間輪算法也存在一些缺點:
- 時間粒度:時間輪算法的時間粒度是固定的,不能靈活地處理不同時間精度的定時任務。
- 精度問題:由於時間輪算法是使用循環隊列來跟蹤時間,因此可能會出現“假溢出”現象,即指針在還沒有到達下一個槽位時就又回到了起點,這會影響定時任務的精度。
- CPU 利用率:時間輪算法需要不斷地更新指針並執行定時任務,這會佔用大量的 CPU 資源。