Timer & TimerTask 深入分析

在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数组,这个数组实现了一个任务队列,越早执行的任务,排在越前面。

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值