Java中Timer的底层机制

Posted by KANG's BLOG on Thursday, April 7, 2022

内部构成说明

Timer

定时器,其维护了一个异步线程TimerThread和一个优先队列TaskQueue,异步线程的作用就是取出任务执行,然后如果是轮训任务,在执行完之后再将其放回队列。

TimerTask

抽象类,继承了Runnable接口,表示单个任务对象,需要使用者实现任务内容。

队列的有序性保证

队列采用数组表示堆结构来保证整体有序性,堆结构的特点是平衡二叉树,任意节点(N)都小于其两个子节点(子节点分别为2N和2N+1),根据这个特性,队列增加或删除元素的方式如下:

  • 增加元素

    直接在数组后面增加元素,如果超过size(默认128)则扩容,增加元素后,不断和父元素比较上移,直到保持整个堆的有序性

  • 删除元素

    当堆顶被取走后,queue[1]需要被删除,那么将队尾元素queue[size]替换到堆顶,然后不断与其两个子节点中较小的进行交换位置,也就是说不断下沉到堆底。

如何调度任务

队列有两个释放锁的条件:

  1. 当队列为空时,将调用queue.wait()方法来等待队列进入任务后通过queue.notify()唤醒
  2. 当队列执行任务后,查询最近执行任务的间隔时间,比如:最近一个任务还差50ms将被执行,那么调用queue.wait(50)来进行等待

Timer的问题

Timer内部仅有一个线程来进行任务调度,当某一任务执行缓慢时,该任务不断循环执行、放回队列、再次被执行,那么将造成其他任务的饥饿问题。

所以更推荐使用ScheduledExecutorService,后者内部使用线程池来执行任务,且对于任务到期判定使用相对时间而不是绝对时间,对系统时间不如Timer敏感。