java时间轮定时器_Kafka 时间轮的原理和实现了解一下

本文详细介绍了Java时间轮定时器的概念,包括简单时间轮和分层时间轮,阐述了它们的工作原理及优缺点。通过Kafka的TimingWheel实现,解释了时间轮如何用于管理定时任务,特别是其在Kafka异步任务处理中的角色。通过时间轮的推进和任务处理,展示了Kafka如何利用时间轮优化插入和删除操作的效率。
摘要由CSDN通过智能技术生成

1 简单时间轮

简单时间轮是时间任务桶的循环链表,又被称为 桶(bucket) 。令 u 为时间单元大小,一个大小为 n 的时间轮有 n 个桶,能够持有 n * u 个定时任务,每个任务的过期时间会落在一个时间间隔内。(注:下文的 u 和 n 沿用这个定义)

每个桶持有进入相应时间范围的定时任务。第一个桶持有 [0, u) 范围的任务,第二个桶持有 [u, 2u) 范围的任务……第 n 个桶持有 [u * (n - 1), u * n) 范围的任务。每过一个时间单元 u,定时器会推进并移动到下个桶,然后第一个桶中所有的定时任务都会过期。由于任务已经过期,此时定时器不会插入任务到当前桶中。定时器会立刻运行过期的任务。因为空桶在下一轮是可用的,所以如果当前的桶对应时间 t,那么它会在推进后变成 [t + u * n, t + (n + 1) * u) 的桶。

本质上时间轮就是个哈希表,通过对 任务的过期时间 求哈希,落到对应的位置。而每个位置对应的 bucket 是链表,因此时间轮插入/删除定时任务 的时间复杂度是 O(1)。而基于优先队列的定时器,比如 java.util.concurrent.DelayQueue 和 java.util.Timer 插入/删除的时间复杂度是 O(log n)。

2 分层时间轮

简单时间轮的主要缺点是它假定定时器请求是在从当前时刻开始的 n * u 时间间隔内,如果定时器请求超出了这个间隔就会产生溢出,导致任务无法放入时间轮中。分层时间轮会处理这种溢出,它以层次来组织时间轮,最底层的精度更高,层数越高,表示的精度更低。这里用 精度 来指代时间单元大小。

举例说明,令 u = 1, n = 3,设起始时刻是 c,则各层次的桶为

a082b40ffea8d071048026d78e2b9a7d.png

PS:这里沿用了代码注释里的表示,即闭区间,而前面讲述原理时都是左闭右开区间,两者是等价的,只是表示不一致。

在 c+1 时刻,桶 [c,c]、[c,c+2]、[c,c+8]过期了,之后:

  • 1 层的时钟移动到 c+1,并且创建新的桶 [c+3,c+3];
  • 2、3 层的时钟仍然在 c 处,因为他们没完全过期。

此时各层次的桶为:

b4f0ca72f4f27451aaf6b99493511829.png

注意,桶 [c,c+2] 不会接收任何任务,因为此时时刻是 c+1,只有过期时间为 c+1 和 c+2 才会被分配到该桶,然而 1 层的两个桶 [c+1,c+1] [c+2,c+2] 会优先接收任务。类似地,3 层的 [c+1,c+8] 也不会接收任何任务,因为这个范围被 2 层的桶覆盖了。

对单层时间轮,插入/删除定时任务的时间复杂度都是 O(1)。对分层时间轮,令 m 是时间轮的数量,则插入的时间复杂度是 O(m),因为最多向上插 m 次。相比起系统中请求的数量,m 通常是小很多的。而删除的时间复杂度是 O(1)。

像时钟就是一个典型的三层时间轮,秒针能表示 0 到 59 秒,但是对 60 秒以上则需要分针进一

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值