大家好,我是Mic,一个没有才华只能靠颜值混饭吃的Java程序员。
一个工作了7年的程序员,去字节面试,被问到时间轮的问题。
他面试回来和我说,这个问题超出了他的知识面,自己也在网上找了一些文章去学习,但是理解不是很深刻。
想让我出一篇关于时间轮问题的面试文章。
谁叫我这么善良呢?立刻就给这个粉丝安排了。
关于“什么是时间轮,请你说一下你对时间轮的理解”这个问题,
我把高手的回答整理到了一个10W字的面试文档里面,大家可以扫描文章尾端二维码领取
下面看看高手的回答
需要高手面试文档面试文档的小伙伴可以扫描文章底部二维码
时间轮,简单理解就是一种用来存储一系列定时任务的环状数组,它的整个工作原理和我们的钟表的表盘类似。
它由两个部分组成, 一个是环状数组,另一个是遍历环状数组的指针。
首先,定义一个固定长度的环状数组,然后数组的每一个元素代表一个时间刻度,假设是1s,那么如果是长度为8的数组,就代表8秒钟。
然后,有一个指针,这个指针按照顺时针无线循环这个数组,每隔最小时间单位前进一个数组索引。
这个指针转一圈代表8秒钟,转两圈表示16秒,假设从0点0分0秒开始,转一圈以后就到了0点0分9秒钟。
环状数组里面的每个元素都是用来存储定时任务的容器,当我们向时间轮里面添加一个定时任务的时候,我们会根据定时任务的执行时间计算它所存储的数组下标。
有可能在某个时间刻度上存在多个定时任务,那这个时候会采用双向链表的方式来存储。
当指针指向某个数组的时候,就会把这个数组中存储的任务取出来,然后遍历这个链表逐个运行里面的任务。
如果某个定时任务的执行时间大于环形数组所表示的长度,一般可以使用一个圈数来表示该任务的延迟执行时间。
也就是说,如果是第16秒要执行的任务,那意味着这个任务应该是在第二圈的数组下标0位置执行。
使用时间轮的方式来管理多个定时任务的好处有很多,我认为有两个核心原因:
减少定时任务添加和删除的时间复杂度,提升性能。
可保证每次执行定时器任务都是O(1)复杂度,在定时器任务密集的情况下,性能优势非常明显。
当然,它也有缺点,对于执行时间非常严格的任务,时间轮不是很适合,因为时间轮算法的精度取决于最小时间单元的粒度。假设以1s为一个时间刻度,那小于1s的任务就无法被时间轮调度。
时间轮算法在很多地方都有用到,比如Dubbo、Netty、Kafka等。
时间轮算法是一个比较有意思的设计。
使用范围比较广,但是在实际应用中,大部分同学接触非常少。
我认为这种设计思想或者这种数据结构,在我们实际应用中的某些特定场景也是可以借鉴和使用。
比如定时重试、衰减重试等。
另外,我将所有Java面试系列制作成了完整的面试文档。它的便捷之处在于,可以通过检索的方式,找到你想要的面试题,目前已经更新180期,总计超过15W字!