zoukankan      html  css  js  c++  java
  • 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) {
            }
        }
    }
    
  • 相关阅读:
    项目人力资源管理
    以太网交换机
    邮件协议简单学习
    信息系统开发方法
    项目成本管理
    oracle学习笔记002---oracle的体系结构
    007 项目进度管理
    乘法逆元
    RMQ __ST
    中国剩余定理(CRT)
  • 原文地址:https://www.cnblogs.com/isawu/p/15268298.html
Copyright © 2011-2022 走看看