Timer和TimerTask源码解读(面试题:双重定时器)

Timer和TimerTask源码解读

Timer是一种定时器工具,用来在一个后台线程计划执行指定任务。它可以计划执行一个任务一次或反复多次。
TimerTask是一个实现了Runnable接口的抽象类,代表一个可以被Timer执行的任务。

Timer和TimerTask基本使用

使用Timer线程实现和计划执行一个任务:

  1. 实现自定义的TimerTask的子类,run方法包含要执行的任务代码。
  2. 实例化Timer类,创建计时器后台线程。
  3. 实例化任务对象 (new RemindTask()).
  4. 制定执行计划。这下面案例中采用schedule方法,第一个参数是TimerTask对象,第二个参数表示开始执行前的延时时间(单位是milliseconds)。还有一种方法可以指定任务的执行时间,例如指定任务在11:01 p.m.执行:
案例:
public class Reminder {
    
    
    Timer timer;

    public Reminder(int seconds) {
    
    
        timer = new Timer();
        timer.schedule(new RemindTask(), seconds * 1000);
    }

    class RemindTask extends TimerTask {
    
    
        public void run() {
    
    
            System.out.println("Time's up!");
            timer.cancel(); 
        }
    }

    public static void main(String args[]) {
    
    
        System.out.println("About to schedule task.");
        new Reminder(5);
        System.out.println("Task scheduled.");
    }
}
Calendar calendar = Calendar.getInstance();
calendar.set(Calendar.HOUR_OF_DAY, 23);
calendar.set(Calendar.MINUTE, 1);
calendar.set(Calendar.SECOND, 0);
Date time = calendar.getTime();

timer = new Timer();
timer.schedule(new RemindTask(), time);

终止Timer线程

默认情况下,只要一个程序的timer线程在运行,那么这个程序就会保持运行。可以通过以下四种方法终止一个timer线程:
  1. 调用timer的cancle方法。你可以从程序的任何地方调用此方法,甚至在一个timer task的run方法里。
  2. 让timer线程成为一个daemon线程(可以在创建timer时使用new Timer(true)达到这个目地),这样当程序只有daemon线程的时候,它就会自动终止运行。
  3. 当timer相关的所有task执行完毕以后,删除所有此timer对象的引用(置成null),这样timer线程也会终止。
  4. 调用System.exit方法,使整个程序(所有线程)终止。

TimerTask任务:

TimerTask的状态有4种:

  1. VIRGIN:未使用的,即初始状态
  2. SCHEDULED:任务被放到TaskQueue,但是还未执行
  3. EXECUTED:只针对只执行一次的TimerTask,表示已经被执行,或者正准备执行
  4. CANCELLED:被取消
public abstract class TimerTask implements Runnable {
    
    
    final Object lock = new Object();//锁
    int state = VIRGIN;
    
    static final int VIRGIN = 0;//VIRGIN:未使用的,即初始状态
    static final int SCHEDULED   = 1;//任务被放到TaskQueue,但是还未执行
    static final int EXECUTED    = 2;//只针对只执行一次的TimerTask,表示已经被执行,或者正准备执行
    static final int CANCELLED   = 3;//被取消

    long nextExecutionTime;//执行时间属性 
    long period = 0;//间隔
    protected TimerTask() {
    
    
    }
    
    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);
        }
    }
}

Timer定时器:

​ Timer定时器的实现原理,是通过Object.wait(timeout),来进行的线程阻塞,timeout是根据下次执行实际和当前实际之差来计算。实际上,可以归结为一个多线程协作问题。

TaskQueue:

TaskQueue中存放一些列将要执行的TimerTask,以数组的形式存放,下标约小(注:下标为0不处理,即使用的最小下标为1),则表明优先级越高。

private final TaskQueue queue = new TaskQueue();

class TaskQueue {
    
    
  	//面有一个 TimerTask 的数组,注释中指出这个队列是一个优先级队列(平衡二叉堆)
  	//按照执行时间进行判断,queue[0]是执行时间最早的任务,反之最晚
    private TimerTask[] queue = new TimerTask[128];
    private int size = 0;
    int size() {
    
    }
  	//加入任务的方法
    void add(TimerTask task) {
    
    
        // Grow backing store if necessary
        if (size + 1 == queue.length)
            queue = Arrays.copyOf(queue, 2*queue.length);//如果queue满容就扩大两倍
        queue[++size] = task;
      	//根据二叉堆特性调整
        fixUp(size);
    }
    TimerTask getMin() {
    
    return queue[1];}
    TimerTask get(int i) {
    
    return queue[i];}
  	//二叉堆删堆顶元素,堆顶放到堆末尾,size--
    void removeMin() {
    
    
      	queue[1] = queue[size];
        queue[size--] = null;  // Drop extra reference to prevent memory leak
        fixDown(1);}
    void quickRemove(int i) {
    
    
        assert i <= size;
        queue[i] = queue[size];
        queue[size--] = null;  // Drop extra ref to prevent memory leak
    }
    void rescheduleMin(long newTime) {
    
    queue[1].nextExecutionTime = newTime;fixDown(1);}
    boolean isEmpty() {
    
    ...}
    void clear() {
    
    ...}
    private void fixUp(int k) {
    
    ...}
    private void fixDown(int k) {
    
    ... }
    void heapify() {
    
    ...}
}

TimerThread:

TimerThread为Thread的扩展类,会一直从TaskQueue中获取下标为1的TimerTask进行执行。并根据该TimerTask是否需要重复执行来决定是否放回到TaskQueue中。

private final TimerThread thread = new TimerThread(queue);

class TimerThread extends Thread {
    
    
  	//还有没有指向 Timer 对象的活跃引用,如果标记为false,且任务队列里没有任务了,就说明可以停止 Timer 的运行
    boolean newTasksMayBeScheduled = true;

    private TaskQueue queue;

    TimerThread(TaskQueue queue) {
    
    
        this.queue = queue;
    }
		
    public void run() {
    
    
        try {
    
    
            mainLoop();//循环取任务执行
        } finally {
    
    
            // Someone killed this Thread, behave as if Timer cancelled
            synchronized(queue) {
    
    
                newTasksMayBeScheduled = false;
                queue.clear();  // Eliminate obsolete references
            }
        }
    }

    private void mainLoop() {
    
    
        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
                    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 {
    
     // Repeating task, reschedule
                                queue.rescheduleMin(
                                  task.period<0 ? currentTime   - task.period
                                                : executionTime + task.period);//重复执行
                            }
                        }
                    }
                    if (!taskFired) // Task hasn't yet fired; wait
                        queue.wait(executionTime - currentTime);
                }
                if (taskFired)  // Task fired; run it, holding no locks
                    task.run(); //执行任务
            } catch(InterruptedException e) {
    
    
            }
        }
    }
}	

schedule()方法:

		// delay 是采用系统时间加上delay的毫秒数来进行的		
		public void schedule(TimerTask task, Date firstTime, long period) {
    
    
        if (period <= 0)
            throw new IllegalArgumentException("Non-positive period.");
        sched(task, firstTime.getTime(), -period);
    }
		以固定速率执行,在delay之后开始
		public void schedule(TimerTask task, long delay, long period) {
    
    
        if (delay < 0)
            throw new IllegalArgumentException("Negative delay.");
        if (period <= 0)
            throw new IllegalArgumentException("Non-positive period.");
        sched(task, System.currentTimeMillis()+delay, -period);
    }

		public void schedule(TimerTask task, Date time) {
    
    
        sched(task, time.getTime(), 0);
    }

		public void schedule(TimerTask task, long delay) {
    
    
        if (delay < 0)
            throw new IllegalArgumentException("Negative delay.");
        sched(task, System.currentTimeMillis()+delay, 0);
    }

Timer和TimerTask双重定时器:

使用定时器,间隔 4 秒执行一次,再间隔 2 秒执行一次

public class DoubleTimer extends TimerTask {
    
    
    int sum = 0;

    public static void main(String[] args) {
    
    
        Timer timer = new Timer();//设置一个定时器
        timer.schedule(new DoubleTimer(),2000);//2秒后执行TimerTest中的run方法
        while (true) {
    
    
            System.out.println(new Date().getSeconds());
            try {
    
    
                Thread.sleep(1000);//间隔一秒
            } catch (InterruptedException e) {
    
    
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
        }
    }

    @Override
    public void run() {
    
    
        sum = (sum +1)%2;//结果01交替
        System.err.println("执行定时任务,隔两秒四秒交替打印");
        new Timer().schedule(new DoubleTimer(), 2000+2000*sum);
    }
}

猜你喜欢

转载自blog.csdn.net/yubo_830/article/details/106454422