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