前言
延時或定時任務在開發中十分常見,Java
也內置了Timer
工具類來實現延時或定時功能,Timer
通過優先隊列來維護延時任務,以延時任務的到期時間作為優先級,通過單線程循環地從隊列中取出到期的任務執行,延時通過wait
方法來實現。
Timer
實現的延時功能在一些簡單的場景是可以的,但是對於一些要求比較高的場景則有些不夠看了。因為通過優先級隊列來維護延時任務,添加和獲取操作的時間復雜度都是O(logn)
,在任務多的情況下,效率是比較低的。
並且,通過單線程來監聽和處理到期的延時任務,如果某個任務執行超時,那么將導致后面的延時任務都推遲執行。所以,為了解決比較高要求的延時場景就有了時間輪模型。下面,來動手實現一個單級時間輪和一個兩級時間輪。
時間輪
時間輪可以以O(1)
的時間復雜度添加和取出到期的延時任務,在執行效率上比優先級隊列要高。
單級時間輪
原理:
通過一個數組來存儲延時任務,當多個延時任務在同一個時刻執行時,組成一個雙向鏈表(為了O(1)
地添加新的延時任務)。再通過一個指針每tick
時間循環地后移,每到達一個位置時,就執行該位置下地延時任務。
多級時間輪
為什么需要多級時間輪?
因為單級時間輪的最大延時時間比較小(受數組元素個數限制),為了適用更多的場景,需要擴大數組大小,但那樣的話內存消耗很大且可能放不下,所以有了多級時間輪。
多級時間輪,比如建立以秒,分鍾,小時為單位的三級時間輪,那么該時間輪最大可以延時一天。不過,多級時間輪也存在一些問題:
(1)當始末時刻時,比如0s(60s)
時,需要從下一級的分鍾級時間輪中加載下一分鍾的延時任務信息到秒級時間輪。如果延時任務比較多,那么必然會導致一定的延遲
(2)多級時間輪同樣也會占用比較大的內存,或者內存存不下
解決辦法其實也不難,在始末時刻到來之前,先預加載未來一分鍾或者一小時的延時任務信息就可以避免加載的延遲。而對於內存存儲不下所有的延時任務,那么可以考慮將實際的延時任務存儲到外存,而內存只存儲索引信息,這樣就可以處理很多延時任務的情況下,內存不夠用的問題。
實現源碼
源碼請看GitHub
倉庫:
github - Flowers-bloom - TimeWheel
如果訪問速度太慢,可訪問Gitee
:
gitee - flowers-bloom - TimeWheel