Skip to the content.

首页

timer实现


Timer (jdk)

不建议使用。


ScheduledThreadPoolExecutor (jdk)

实现基于ThreadPoolExecutor,使用的阻塞队列为DelayedWorkQueue,任务类型为ScheduledFutureTask。

任务提交均直接加入队列,并使用ensurePrestart方法确保存在运行中的worker,保证了任务一定是被worker从队列中取出执行。

DelayedWorkQueue

与DealyQueue基本一致,是基于延迟时间排序的二叉堆,并通过ReentrantLock和Condition阻塞worker直到任务能被执行;不同是由于ScheduledFutureTask中记录了其在堆中的索引,故在移除任务时不再需要遍历查找,提高了效率。


HashedWheelTimer (netty)

单层时间轮,不支持周期性任务。

newTimeout方法

  1. pendingTimeoutsCount自增,如果当前等待任务数已超出最大限制则抛出异常;
  2. 调用start方法启动工作线程,为幂等方法,即HashedWheelTimer不需要显示启动;
  3. 计算剩余延时时间并创建HashedWheelTimeout,添加到队列中,并没有添加到时间轮中。

HashedWheelTimeout

Worker

工作线程流程,启动时获取当前纳秒数,记录工作线程启动的时间,之后以每格的时间为速率执行流程:

  1. processCancelledTasks:处理取消的任务,从待取消任务队列中取出任务,并执行其remove方法。

  2. transferTimeoutsToBuckets:将等待任务队列中的任务取出并加入时间轮中,任务直接添加到格子所属链表的尾端,限制了每次最多处理100000个任务。

  3. HashedWheelBucket.expireTimeouts:当前格执行任务,遍历链表,任务到期则移除并执行其expire方法,否则轮数减1。


TimerWheel (caffeine)

多层时间轮,BoundedLocalCache如果设置了expireAfter,会创建一个WheelTimer对象用于移除过期键。储存为Sentinal对象(Node链表头节点)二维数组,上层时间轮的一格等于下一层整个时间轮的跨度。

advance(long currentTimeNanos)

通过传入的currentTimeNanos和上次执行时的nanos,计算出每层时间轮需要处理的格子区间,对区间里的所有节点调用BoundedLocalCache的evictEntry方法做过期移除,如未过期则重新加入时间轮。


延迟队列VS时间轮算法

时间轮适合处理大量小任务,因为延迟队列使用了堆,调整堆的时间复杂度为O(logn),而时间轮算法为O(1),但空间消耗大。