场景
netty内部有一个时间轮的Timer来管理大量的定时调度,例如管理心跳检测
使用方法
1 | <dependency> |
1 | public static void main(String[] args) throws InterruptedException { |
output
start:2018-06-01 18:33:41
task1 cancel :2018-06-01 18:33:56
task2 :2018-06-01 18:34:11
时间轮算法
经典图
时间轮一圈分为多个格子,每个格子代表一段时间,这个时间长短影响Timer精度,格子中包含落在这个格子的任务,每个任务带有一个round轮数,指针转动到一个格子,会检查任务的round,等于0则执行任务;任务添加到格子中需要根据一定的规则例如取模或者startTime等计算出deadline; netty timer的做法是在从一个队列中增量的添加到格子,根据deadline的计算。
多级时间轮
单层的时间轮,在极端情况下,单个格子的链表很长,所以有了多级的时间轮设计 FIXME
kafka分级时间轮
性能测试评估
添加任务 O(1)
取消任务 O(1)
执行任务 最差O(n),格子越少,则链表越长,时间越长,如果格子越大,则链表越短,空间消耗越大
性能测试 FIXME
类图和时序图
- Timer 接口
提供newTimeout方法定义创建新的任务,stop方法定义停止时间轮; - HashedWheelTimer 核心类:
Timer的实现,创建时间轮的结构,初始化一个任务队列和一个取消的任务队列、格子bucket数组、格子大小tickDuration,创建work调度线程,当调用newTimeout时,判断work线程状态,第一次则启动调度线程; - HashedWheelBucket 格口类:
保存任务类的链表结构,提供给队列放入timeout任务的方法,提供work调度执行任务的方法,提供执行完后或取消移除任务的方法; - HashedWheelTimeout 任务包装类:
提供链表结构基础,提供任务的取消移除方法,以及任务状态的维护,提供任务的执行入口; - Worker 调度线程类:
时间轮指针转动的调度逻辑,转动一个格子,将任务从队列中补充到该bucket中,处理round=0的任务,再处理已取消的任务;
时序图 FIXME
代码细节
创建HashedWheelTimer
初始化时间轮
1 | // 构造函数 |
create HashedWheelBucket[]
1 | // createWheel |
标准化格子参数大小
1 | // normalizeTicksPerWheel |
Timer添加timeout任务、启动
newTimeout
1 |
|
时间轮start
1 | public void start() { |
时间轮stop
1 |
|
时间轮调度逻辑
1 |
|
HashedWheelBucket的链表结构
1 | private static final class HashedWheelBucket { |
扩展知识点、思考 FIXME
确定不大于2^n数字方法
链表结构设计
睡眠时间精度问题
JCTool的并发队列
存储泄漏检测
数值精度的判断
Atomic* 类的CAS并发无锁更新
AtomicIntegerFieldUpdater的CAS
线程之间等待的操作 startTime初始化 CountDownLatch