Timer-计时器

Timer 线程调度任务

本质上每个Timer对象都是一个单个后台线程Thread,用于依次执行该对象的所有任务。当Timer对象被new出来时,后台线程就会启动,没有任务会wait(),直到添加任务后被唤醒。
添加的任务应该是能很快完成的。如果某个任务执行时间过长(超过间隔时间period),因为Timer是单线程,它会导致后面任务的执行时间被延迟执行和快速执行。

假如我们创建了一个定时器对象Timer,设置执行间隔period=10s。
我们的理想状态是,每个任务完成的时间小于10s,这样每个任务执行的时间轴是0,10,20,30...
但有可能每个任务执行的时间有波动,导致第一次执行时间大于10s且结束后会立即执行第二次任务,也会影响后续的任务执行时间,达不到一个定时的效果(任务只会延后,不会提前执行)。

构造器

public Timer(String name, boolean isDaemon) {
    // 定时器名称(线程名)
    thread.setName(name);
    // 是否设置定时器为守护线程,默认是false 
    thread.setDaemon(isDaemon);
    // 对象创建好立即执行
    thread.start();
}

说一下isDemon作用,如果我们在main线程中创建定时器(创建好是立即在后台执行的),这时候即使main线程结束了,定时器仍会在执行。如果setDemon(true),则定时器是一个后台执行的守护线程,main线程如果结束了,定时器也会立即结束。

成员参数

// 定时器任务队列,与TimerThread共享
private final TaskQueue queue = new TaskQueue();

// Timer后台执行线程
private final TimerThread thread = new TimerThread(queue);

方法

// 一次性任务
public void schedule(TimerTask task, Date time);

// period传递给sched方法为负数
public void schedule(TimerTask task, long delay, long period);
public void schedule(TimerTask task, Date firstTime, long period);

// period传递给sched方法为正数
public void scheduleAtFixedRate(TimerTask task, long delay, long period);
public void scheduleAtFixedRate(TimerTask task, Date firstTime, long period);

// 终止定时器
public void cancel();

// 清理queue中cancelled状态的任务,返回删除的数量
public int purge()

TimerTask

能被Timer调度一次或多次的任务抽象类,实现了Runnable接口;另外同一个TimerTask能被不同Timer调度

成员参数

// 一个TimerTask对象能被多个Timer调度,修改该对象成员变量时需要使用synchronized进行同步
final Object lock = new Object();

/**
  实例对象自己维护了一个状态state:
  VIRGIN :刚创建的TimerTask默认值(初始值)
  SCHEDULED :调用schedule方法放入queue前,设置peiod、nextExecutionTime、state
  EXECUTED :非重复任务已执行或正在执行中,还未被取消
  CANCELLED :任务已被取消(调用cancel)
*/
int state = VIRGIN;

// 下次执行时间,格式System.currentTimeMillis
// 对于重复任务,该字段在每次任务执行之前更新
long nextExecutionTime;

// 重复任务的执行间隔时间 毫秒ms
// 如果是正数,表示固定速度执行(每次执行时间在创建时就确定了,时间轴不变)
// 如果是负数,表示固定延迟执行(相对上次执行时间固定间隔,时间轴可能会变大)
long period = 0;

方法

// 实现定时任务的业务逻辑
public abstract void run();

// 取消任务,返回当前state==SCHEDULED,并将state设置为CANCELLED
public boolean cancel();

TaskQueue

Timer的内部优先队列,是一个二叉堆,按nextExecutionTime排序的最小堆。

这边讲个小知识,优先队列虽然本质是个完全二叉树,但实现是通过数组。每次取queue[1],有人可能会问为什么不是取queue[0]呢?因为一棵深度为n的完全二叉树节点总个数是2n-1,数组长度一般都是2n,所以数组多出一个位置queue[0]不存放数据。这样还有个好处,如果一个二叉树节点的数组位置是queue[i],那么它两个左右子节点的可以分别表示成queue[2i],queue[2i+1]。

TimerThread

Timer内部线程对象,继承于Thread

成员变量

// 
boolean newTasksMayBeScheduled = true;

// 
private TaskQueue queue;

方法

// 不废话,直接干(TimerThread的run方法体)
private void mainLoop() {
    // Timer后台线程一直执行
    while (true) {
        try {
            TimerTask task;
            boolean taskFired;
            synchronized(queue) {
                // 任务队列为空 线程进行等待
                while (queue.isEmpty() && newTasksMayBeScheduled)
                    queue.wait();
                // 等待结束后,队列仍为空则终止定时器
                if (queue.isEmpty())
                    break; // Queue is empty and will forever remain; die

                // Queue nonempty; look at first evt and do the right thing
                long currentTime, executionTime;
                // 取出队列中优先级最高的任务,但不从队列中移除
                task = queue.getMin();
                synchronized(task.lock) {
                    // 判断任务是否被取消
                    if (task.state == TimerTask.CANCELLED) {
                        queue.removeMin(); // 移除该任务
                        continue;  // No action required, poll queue again
                    }
                    currentTime = System.currentTimeMillis();
                    executionTime = task.nextExecutionTime;
                    // 判断任务是否到达执行的时间
                    if (taskFired = (executionTime<=currentTime)) {
                        // 一次性任务,执行完丢弃
                        if (task.period == 0) { // Non-repeating, remove
                            queue.removeMin();
                            task.state = TimerTask.EXECUTED;
                        } else { // 重复性任务,需要
                            queue.rescheduleMin(
                                 // 设置该任务下次的执行时间nextExecutionTime
                                 // period<0 固定延迟:当前时间+period
                                 // period>0 固定速率:预期执行时间+period
                                 task.period<0 ? currentTime - task.period : executionTime + task.period);
                        }
                    }
                }
                if (!taskFired) // 任务还未到执行时间,继续等待固定时长
                    queue.wait(executionTime - currentTime);
            }
            if (taskFired)  // Task fired; run it, holding no locks
                task.run(); // 执行任务
        } catch(InterruptedException e) {
        }
    }
}
原文地址:https://www.cnblogs.com/isawu/p/15268298.html