Timer & TimerTask 深入分析

来源:互联网 发布:广联达软件怎么用 编辑:程序博客网 时间:2024/06/05 07:36

在java中,有一个常用的定时机制,Timer&TimerTask。你可以方便地:

1)在某个时刻做某件事情, public void schedule(TimerTask task, Date when);

2)在一段延迟后做某件事情,public void schedule(TimerTask task, long delay);

在这两种方式中,都可以指定做这件事情的频率,用以下两个方法:public void schedule(TimerTask task, long delay, long period);public void schedule(TimerTask task, Date when, long period);

废话少说,还是看个例子吧:

import java.io.IOException;import java.util.Timer;public class TimerTest {        public static void main(String[] args) {        Timer timer = new Timer();        timer.schedule(new MyTask(), 1000, 2000);  //在1秒后执行此任务,每次间隔2秒,如果传递一个Data参数,就是在某个固定的时间执行这个任务.               while(true) {                           try {                  int ch = System.in.read();                  if(ch-'c'==0) {                      timer.cancel();  //结束任务                  }              } catch (IOException e) {                  e.printStackTrace();              }        }}static class MyTask extends java.util.TimerTask {   // 这是你的任务线程     @Override     public void run() {         // accomplish your task here     }}


可以说,Timer 提供了一个异步任务的启动机制。说到异步,呵呵,肯定就有多线程啦。这里涉及到了2个或者说是3个线程:

1)当前线程,也就是TimerTest 中 Main 方法跑的那个线程,这个不需要多说,都懂的。

2)任务线程,也就是MyTask 线程。这个也不复杂,看看TimerTask的实现就知道了:

public abstract class TimerTask implements Runnable {    ***    ***    public abstract void run();}


问题是这个任务是单独开了一个线程吗?不急,后面有解答。

3)Timer 线程; 

Timer 本身还单独开了一个线程吗?是的!看看Timer 的代码就明白了。策略模式。Timer 将具体的功能实现委托给静态内部类TimerImpl。Timer 类中有一个 静态内部类 TimerImpl,而它又继承了Thread,让我们接着来分析下这个TimerImple 子线程的生命周期:

创建:当Timer 实例构造的时候,子线程被启动;那么它什么时候结束的呢?实际上就是看看TimerImpl.run() 方法中的while(true) 循环是怎么退出的。

a) 当Timer.cancel() 被调用时,cancelled 标志被置为true,循环退出,线程结束;

b)当某个task的run() 方法异常退出时,cancelled 也会被置为 true,循环退出,线程结束。

                try {                    task.run();                    taskCompletedNormally = true;                } finally {                    if (!taskCompletedNormally) {                        synchronized (this) {                            cancelled = true;                        }                    }                }

以上代码还揭示了一个容易被忽视的真相,看看task.run(); 你是不是明白了什么,原来 TimerTask 虽然实现了Runnable 接口,但TimerTask并没有被start,而是被直接运行run() 方法。所以,TimerTask并没有单独开一个线程,而是就跑在TimerImpl 子线程中的。基于这样的事实,你不能在你的TimeTask的run() 方法中做过于耗时的操作,否则可能影响到其他task的按时执行。

呵呵,也许你会奇怪了,Runnable 不就是为多线程机制准备的吗?嗯,Runnable 主要是为多线程机制准备的,但不尽然,看看它的定义和注释吧:

/** * Represents a command that can be executed. Often used to run code in a * different {@link Thread}. */public interface Runnable {    /**     * Starts executing the active part of the class' code. This method is     * called when a thread is started that has been created with a class which     * implements {@code Runnable}.     */    public void run();}


看见最上面的那行注释了吧,通常(often)运行在另外一个线程中,但并非总是(always)。所以,这个run未必总是跑在另外一个线程中的。其实,Runnable的这种看似另类的用法,也是比较常用的。

c)当TimerTask 队列为空,且TimerImple.finished 标志被置为true。而这个标志什么时候被置为true的呢,这个问题很有意思。看看Timer的另外一个静态内部类:

    private static final class FinalizerHelper {        private final TimerImpl impl;        FinalizerHelper(TimerImpl impl) {            this.impl = impl;        }        @Override protected void finalize() throws Throwable {            try {                synchronized (impl) {                    impl.finished = true;                    impl.notify();                }            } finally {                super.finalize();            }        }    }


当 FinalizerHelper 的实例,也就是Timer的一个私有成员变量(finalizer)被GC时,FinalizerHelper.finalize() 方法被JVM调用到,TimerImple.finished 标志被置为true。可以看到,Timer.finalizer 并没有被引用到,所以它应该是最早被GC掉的,这确保了它能完成一个光荣使命 ~~~~~~~ 帮助杀死TimerImpl 线程。

 

Timer 对Task 的管理

在TimerImple 对 Task 的具体管理操作又委托给了它自己的一个内部类 TimerHeap。在TimerHeap 内部定义了一个可动态扩展的TimerTask数组,这个数组实现了一个任务队列,越早执行的任务,排在越前面。