Timer和TimerTask都已经有所了解了,那么写一个简单的例子看看效果。 定时任务类 package timer;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.TimerTask;
public class MyTimerTask extends TimerTask{
private String name;
public MyTimerTask(String name){
this.name=name;
}
@Override
public void run() {
System.out.println(name+"在执行....."+new SimpleDateFormat("yyyy-MM-dd hh:mm:ss").format(new Date()));
}
}
定时执行任务类的测试类 package timer;
import java.util.Timer;
public class TimerTest {
public static void main(String[] args) {
Timer t=new Timer();
t.schedule(new MyTimerTask("A"), 1000,2000);//1秒后执行该任务,然后每2秒执行1次
t.schedule(new MyTimerTask("B"),1000,3000);
}
}
运行结果: A在执行.....2013-05-11 11:04:10 B在执行.....2013-05-11 11:04:10 A在执行.....2013-05-11 11:04:12 B在执行.....2013-05-11 11:04:13 A在执行.....2013-05-11 11:04:14 A在执行.....2013-05-11 11:04:16 B在执行.....2013-05-11 11:04:16 A在执行.....2013-05-11 11:04:18 执行结果打印的时间,同一个任务的间隔时间可能不等于你设置的间隔时间,这就涉及到CPU对线程的执行是轮换执行的,这方面的知识,读者可以去了解多线程方面的知识。 下面我们一起来浏览一下相关源码。我们要使用Timer就首先要new一个,则先看看Timer的构造方法源码。 public Timer() {
this("Timer-" + serialNumber());
}
public Timer(boolean isDaemon) {
this("Timer-" + serialNumber(), isDaemon);
}
public Timer(String name) {
thread.setName(name);
thread.start();
}
public Timer(String name, boolean isDaemon) {
thread.setName(name);
thread.setDaemon(isDaemon);
thread.start();
}
发现实例化Timer对象时,它会启动一个TimerThread类的线程,那继续看看TimerThread类中的run方法。 public void run() {
try {
mainLoop();
} finally {
// Someone killed this Thread, behave as if Timer cancelled
synchronized(queue) {
newTasksMayBeScheduled = false;
queue.clear(); // Eliminate obsolete references
}
}
} 它调用了mainLoop()方法,那就继续跟踪. private void mainLoop() {
while (true) {
try {
TimerTask task;
boolean taskFired;
synchronized(queue) {//锁住定时任务队列
// Wait for queue to become non-empty
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();//任务只执行一次,就是在使用Timer的schedule()方法时,
//没有指定每隔几秒执行一次任务,则默认执行一次,然后就从队列中删除
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) {
}
}
}
} 到这为止,已经知道new Timer()时JVM就会干哪些事情了。那接下来看看Timer类中schedule()方法。 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);
}
这只是schedule()重载方法中的一个,但是所有的schedule()重载方法里面都会调用sched()这个方法,接下来看看sched()方法。上面有个疑问,队列进入阻塞后,谁来唤醒呢?呵呵,看下面源码 private void sched(TimerTask task, long time, long period) {
if (time < 0)//如果指定的执行时间<0就抛出异常
throw new IllegalArgumentException("Illegal execution time.");
synchronized(queue) {//锁住任务队列
if (!thread.newTasksMayBeScheduled)//检查任务的定时器是否还在,如果被取消了,则任务不能加入队列
throw new IllegalStateException("Timer already cancelled.");
synchronized(task.lock) {//锁住即将要加入队列的任务
if (task.state != TimerTask.VIRGIN)
throw new IllegalStateException(
"Task already scheduled or cancelled");
task.nextExecutionTime = time;//任务的执行时间
task.period = period;//任务第一次执行后,每隔多长时间执行第二次,如果period<=0,在任务执行一次后,就被取消,从队列中删除
task.state = TimerTask.SCHEDULED;
}
queue.add(task);//定时任务加入队列
if (queue.getMin() == task)//获得队列中的头任务
//还记得上面给出的mainLoop()方法的源码吗?
//如果队列为空但即将有任务进来,队列就阻塞wait();
//或者头任务的执行时间还没到,队列也阻塞wait();
queue.notify();//唤醒队列,有新任务进来了
}
} |