Timer类

        Timer 类位于java.util包下,其主要依托TimerTask,TaskQueue,TimerThread 来实现延时一次性任务 或者 间隔一段时间后重复执行。JDK1.5之后推荐使用 ScheduledThreadPoolExecutor,下一篇文章讲。

public class Timer {

    //创建一个静态TaskQueue优先级队列(由小根堆实现),队列里存储的是TimerTask数组
    private final TaskQueue queue = new TaskQueue();

    /**
     * The timer thread.
     */
    private final TimerThread thread = new TimerThread(queue);

    
    public Timer(String name) {
        thread.setName(name);
        thread.start();
    }
}

        其大体如下图:

1、TimerTask

        TimerTask是一个抽象类,implements Runnable接口,从而提供执行线程任务。主要是来约束任务状态,锁、任务执行时间等。

public abstract class TimerTask implements Runnable {
    /**
     * 内部维护一个对象锁,因为在TimerThread中一直循环判断是否要执行TimerTask,防止多线程问题
     */
    final Object lock = new Object();

    /**
     * 任务状态,从下面的几个常量选择0,1,2,3.默认是0
     */
    int state = VIRGIN;

    /**
     * 状态:还没有执行
     */
    static final int VIRGIN = 0;

    /**
     * 计划执行此任务。如果是一次性(不重复)任务,还没有执行
     */
    static final int SCHEDULED   = 1;

    /**
     * 不重复的任务已经执行(或者正在执行),并且尚未取消
     */
    static final int EXECUTED    = 2;

    /**
     * 任务取消(调用了TimerTask.cancel方法)
     */
    static final int CANCELLED   = 3;

    /**
     * 下一次执行任务的时间。对于重复执行任务,任务执行后,会根据period字段进行更新(为了下次执行)
     */
    long nextExecutionTime;

    /**
     * 重复任务的周期(以毫秒为单位)。正值表示固定速率执行。负值表示固定延迟执行。值0表示不重复        
     * 的任务。
     */
    long period = 0;

    protected TimerTask() {
    }

    /**
     *   run抽象方法
     */
    public abstract void run();

    /**
     * 取消任务调用
     */
    public boolean cancel() {
        synchronized(lock) {
            boolean result = (state == SCHEDULED);
            state = CANCELLED;
            return result;
        }
    }

    /**
     * 返回此任务最近一次实际执行的计划执行时间。(如果在任务执行过程中调用此方法,则返回值为正在            
     * 执行的任务的计划执行时间。)
     */
    public long scheduledExecutionTime() {
        synchronized(lock) {
            return (period < 0 ? nextExecutionTime + period
                               : nextExecutionTime - period);
        }
    }
}

        其中关于TimerTask中period字段,当其字段值为0是为非重复性任务啊(执行一次),否则是重复性任务。正值表示固定速率执行。负值表示固定延迟(固定间隔)执行。对于nextExecutionTime  字段,假设一个任务的TimerTask字段中 nextExecutionTime 时间 小于等于当前时间,说明这个任务该执行了,判断是重复任务的话,更新字段 nextExecutionTime 。更新规则如下:

TimerTask task;  
task.executionTime  = task.period < 0 ? currentTime(当前时间戳,任务执行开始的时间) - task.period :         
  executionTime + task.period

        解释一下固定速率和固定间隔区别:

        固定速率(fixed-rate):匀速执行任务(无论这个任务是否完成,时间到了都执行,下次执行时间为 :currentTime(当前时间戳,任务执行开始的时间) - task.period  )

        固定间隔(fixed-delay):任务执行结束后,等待间隔时间后执行(但是在TimerThread类中run方法的fixed-delay 不是 执行结束时间 + period,而是直接拿的字段excutionTime值 + period,没搞懂)

        

2、TaskQueue

        TaskQueue维护了一个TimerTask数组(使用优先级队列,小根堆),实现TimerTask根据字段 nextExecutionTime 来排序队列,提供啊add 、 getMin、get、removeMIn等方法。

其源代码:

class TaskQueue {
    /**
     * 维护TimerTask队列,从1开始
     */
    private TimerTask[] queue = new TimerTask[128];

    /**
     * 队列中存在任务数量
     */
    private int size = 0;

    /**
     * 返回队列中存在任务数量
     */
    int size() {
        return size;
    }

    /**
     * 添加task到队列
     */
    void add(TimerTask task) {
        // 扩容
        if (size + 1 == queue.length)
            queue = Arrays.copyOf(queue, 2*queue.length);

        queue[++size] = task;
        fixUp(size);
    }

    /**
     * 返回队列顶部,小根堆的顶部,下次执行时间最小的任务,也就是距离当前时间执行任务最近的
     */
    TimerTask getMin() {
        return queue[1];
    }


    TimerTask get(int i) {
        return queue[i];
    }

    /**
     * 移除头部,不解释fixDown和fixUp
     */
    void removeMin() {
        queue[1] = queue[size];
        queue[size--] = null;  // Drop extra reference to prevent memory leak
        fixDown(1);
    }

   


    /**
     * 判断队列是否为空
     */
    boolean isEmpty() {
        return size==0;
    }

    /**
     * 清空
     */
    void clear() {
        // Null out task references to prevent memory leak
        for (int i=1; i<=size; i++)
            queue[i] = null;

        size = 0;
    }

   
    private void fixUp(int k) {
        while (k > 1) {
            int j = k >> 1;
            if (queue[j].nextExecutionTime <= queue[k].nextExecutionTime)
                break;
            TimerTask tmp = queue[j];  queue[j] = queue[k]; queue[k] = tmp;
            k = j;
        }
    }

 
    private void fixDown(int k) {
        int j;
        while ((j = k << 1) <= size && j > 0) {
            if (j < size &&
                queue[j].nextExecutionTime > queue[j+1].nextExecutionTime)
                j++; // j indexes smallest kid
            if (queue[k].nextExecutionTime <= queue[j].nextExecutionTime)
                break;
            TimerTask tmp = queue[j];  queue[j] = queue[k]; queue[k] = tmp;
            k = j;
        }
    }

}

3、TimerThread

        顾名思义线程,继承了Thread类,计时器的任务执行线程,该线程等待计时器队列中的任务,在它们到时执行它们,重新安排重复任务,并从队列中删除已取消的任务和已使用的非重复任务。需要TimerQueue提供任务队列。它需要单独一个线程来执行run方法,不断判断队列中的任务状态和执行时间。源代码如下:

class TimerThread extends Thread {
    /**
     * false时,说明已经没有引用,清空队列。
     */
    boolean newTasksMayBeScheduled = true;

    private TaskQueue queue;

    TimerThread(TaskQueue queue) {
        this.queue = queue;
    }

    public void run() {
        try {
            mainLoop();
        } finally {
            // 线程被杀死,或者队列任务执行完了
            synchronized(queue) {
                newTasksMayBeScheduled = false;
                queue.clear();  // Eliminate obsolete references
            }
        }
    }

    /**
     * The main timer loop. 
     */
    private void mainLoop() {
        while (true) {
            try {
                TimerTask task;
                //任务是否该执行
                boolean taskFired;
                // 添加队列锁
                synchronized(queue) {
                    // 队列是空的,并且没有清除引用,等待任务添加到队列
                    while (queue.isEmpty() && newTasksMayBeScheduled)
                        queue.wait();
                    if (queue.isEmpty())
                        break; // 还是空,清空

                    // 拿去队列中第一个任务,也就是最先执行的任务
                    long currentTime, executionTime;
                    task = queue.getMin();
                    synchronized(task.lock) {
                        if (task.state == TimerTask.CANCELLED) {
                            queue.removeMin();
                            continue;  // 任务状态是已经取消,移出队列第一个任务,循环
                        }
                        currentTime = System.currentTimeMillis();
                        executionTime = task.nextExecutionTime;
                        if (taskFired = (executionTime<=currentTime)) {
                            if (task.period == 0) { // 任务不是重复性的,直接移出队列
                                queue.removeMin();
                                task.state = TimerTask.EXECUTED;
                            } else { // 重复性任务,设置任务下一次运行时间
                                queue.rescheduleMin(
                                  task.period<0 ? currentTime   - task.period
                                                : executionTime + task.period);
                            }
                        }
                    }
                    if (!taskFired) // 任务还没到时间执行,等到指定时间执行,wait锁会自动唤醒
                        queue.wait(executionTime - currentTime);
                }
                if (taskFired)  // 任务可以执行
                    task.run();
            } catch(InterruptedException e) {
            }
        }
    }
}

总结

        弊端:因为采用的是优先级队列,插入删除时间复杂度logn,频繁的出入队列性能需要考虑;单线程执行;没有异常处理;

        ScheduledThreadPoolExecutor允许多线程。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值