上面介绍的 TimingWheel 提供了添加延时任务和推进时间轮指针的操作,而具体执行延时任务的操作则交由定时器 SystemTimer 完成。SystemTimer 类实现了 Timer 特质,该特质描绘了定时器应该具备的基本方法。Timer 接口定义了管理延迟操作的方法,而 SystemTimer 是实现延迟操作的关键代码。

Timer 接口类

Timer 接口定义如下:

trait Timer {
  /**
    * Add a new task to this executor. It will be executed after the task's delay
    * (beginning from the time of submission)
    * @param timerTask the task to add
    */
  // 将给定的定时任务插入到时间轮上,等待后续延迟执行
  def add(timerTask: TimerTask): Unit

  /**
    * Advance the internal clock, executing any tasks whose expiration has been
    * reached within the duration of the passed timeout.
    * @param timeoutMs
    * @return whether or not any tasks were executed
    */
  // 向前推进时钟,执行已达过期时间的延迟任务
  def advanceClock(timeoutMs: Long): Boolean

  /**
    * Get the number of tasks pending execution
    * @return the number of tasks
    */
  // 获取时间轮上总的定时任务数
  def size: Int

  /**
    * Shutdown the timer service, leaving pending tasks unexecuted
    */
  // 关闭定时器
  def shutdown(): Unit
}

SystemTimer 类

        SystemTimer 类是 Timer 接口的实现类,它的定义如下:

class SystemTimer(executorName: String, // Purgatory 的名字
                  tickMs: Long = 1, // 默认时间格时间为 1 毫秒
                  wheelSize: Int = 20, // 默认时间格大小为 20
                  startMs: Long = Time.SYSTEM.hiResClockMs // 该 SystemTimer 定时器启动时间
                 ) extends Timer {

  // timeout timer
  // 单线程的线程池用于异步执行定时任务
  private[this] val taskExecutor = Executors.newFixedThreadPool(1, new ThreadFactory() {
    def newThread(runnable: Runnable): Thread =
      KafkaThread.nonDaemon("executor-"+executorName, runnable)
  })
  // 延迟队列保存所有Bucket,即所有TimerTaskList对象
  private[this] val delayQueue = new DelayQueue[TimerTaskList]()
  // 总定时任务数
  private[this] val taskCounter = new AtomicInteger(0)
  // 时间轮对象
  private[this] val timingWheel = new TimingWheel(
    tickMs = tickMs,
    wheelSize = wheelSize,
    startMs = startMs,
    taskCounter = taskCounter,
    delayQueue
  )

  // Locks used to protect data structures while ticking
  // 维护线程安全的读写锁
  private[this] val readWriteLock = new ReentrantReadWriteLock()
  private[this] val readLock = readWriteLock.readLock()
  private[this] val writeLock = readWriteLock.writeLock()
}

        SystemTimer 类最重要的方法是 add 和 advanceClock 方法,因为它们是真正对外提供服务的。

add 方法

        作用是将给定的定时任务插入到时间轮中进行管理。

def add(timerTask: TimerTask): Unit = {
    // 获取读锁。在没有线程持有写锁的前提下,
    // 多个线程能够同时向时间轮添加定时任务
    readLock.lock()
    try {
      // 调用addTimerTaskEntry执行插入逻辑
      addTimerTaskEntry(new TimerTaskEntry(timerTask, timerTask.delayMs + Time.SYSTEM.hiResClockMs))
    } finally {
      // 释放读锁
      readLock.unlock()
    }
  }
// add 方法就是调用 addTimerTaskEntry 方法执行插入动作  
private def addTimerTaskEntry(timerTaskEntry: TimerTaskEntry): Unit = {
    // 视timerTaskEntry状态决定执行什么逻辑:
    // 1. 未过期未取消:添加到时间轮
    // 2. 已取消:什么都不做
    // 3. 已过期:提交到线程池,等待执行
    // TimingWheel 的 add 方法会在定时任务已取消或已过期时,返回 False,否则,该方法会将定时任务添加到时间轮,然后返回 True。
    if (!timingWheel.add(timerTaskEntry)) {
      // Already expired or cancelled
      // 定时任务未取消,说明定时任务已过期
      // 否则timingWheel.add方法应该返回True
      if (!timerTaskEntry.cancelled)
        taskExecutor.submit(timerTaskEntry.timerTask)
    }
  }

 advanceClock 方法

        顾名思义,它的作用是驱动时钟向前推进

/** 将延时任务重新添加到时间轮中 */
  private[this] val reinsert = (timerTaskEntry: TimerTaskEntry) => addTimerTaskEntry(timerTaskEntry)

  /*
   * Advances the clock if there is an expired bucket. If there isn't any expired bucket when called,
   * waits up to timeoutMs before giving up.
   */
  def advanceClock(timeoutMs: Long): Boolean = {
    // 获取delayQueue中下一个已过期的Bucket
    var bucket = delayQueue.poll(timeoutMs, TimeUnit.MILLISECONDS)
    if (bucket != null) {
      // 获取写锁
      // 一旦有线程持有写锁,其他任何线程执行add或advanceClock方法时会阻塞
      writeLock.lock()
      try {
        while (bucket != null) {
          // 推动时间轮向前"滚动"到Bucket的过期时间点
          timingWheel.advanceClock(bucket.getExpiration())
          // 将该Bucket下的所有定时任务重写回到时间轮
          bucket.flush(reinsert)
          // 读取下一个Bucket对象
          bucket = delayQueue.poll()
        }
      } finally {
        // 释放写锁
        writeLock.unlock()
      }
      true
    } else {
      false
    }
  }