Timer详解

概览

这个小例子可以说明一些用Timer线程实现和计划执行一个任务的基础步骤:
  • 实现自定义的TimerTask的子类,run方法包含要执行的任务代码
  • 实例化Timer类,创建计时器后台线程。
  • 实例化任务对象 (new RemindTask()).
  • 制定执行计划。这里用schedule方法,第一个参数是TimerTask对象,第二个参数表示开始执行前的延时时间(单位是milliseconds,这里定义了5000)。还有一种方法可以指定任务的执行时间,如下例,指定任务在11:01 p.m.执行:
//Get the Date corresponding to 11:01:00 pm today.
Calendar calendar = Calendar.getInstance();
calendar.set(Calendar.HOUROFDAY, 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线程:
  • 调用timer的cancle方法。你可以从程序的任何地方调用此方法,甚至在一个timer task的run方法里。
  • 让timer线程成为一个daemon线程(可以在创建timer时使用new Timer(true)达到这个目地),这样当程序只有daemon线程的时候,它就会自动终止运行。
  • 当timer相关的所有task执行完毕以后,删除所有此timer对象的引用(置成null),这样timer线程也会终止。
  • 调用System.exit方法,使整个程序(所有线程)终止。
Reminder的例子使用了第一种方式。在这里不能使用第二种方式,因为这里需要程序保持运行直到timer的任务执行完成,如果设成daemon,那么当main线程结束的时候,程序只剩下timer这个daemon线程,于是程序不会等timer线程执行task就终止了。
有些时候,程序的终止与否并不只与timer线程有关。举个例子,如果我们使用AWT来beep,那么AWT会自动创建一个非daemon线程来保持程序的运行。下面的代码我们对Reminder做了修改,加入了beeping功能,于是我们需要加入System.exit的调用来终止程序。
import java.util.Timer;
import java.util.TimerTask;
import java.awt.Toolkit;

/**
* Simple demo that uses java.util.Timer to schedule a task to execute
* once 5 seconds have passed.
*/

public class ReminderBeep {
    Toolkit toolkit;
    Timer timer;

    public ReminderBeep(int seconds) {
        toolkit = Toolkit.getDefaultToolkit();
        timer = new Timer();
        timer.schedule(new RemindTask(), seconds*1000);
    }

    class RemindTask extends TimerTask {
        public void run() {
            System.out.println("Time's up!");
    toolkit.beep();
    //timer.cancel(); //Not necessary because we call System.exit
    System.exit(0);   //Stops the AWT thread (and everything else)
        }
    }

    public static void main(String args[]) {
System.out.println("About to schedule task.");
        new ReminderBeep(5);
System.out.println("Task scheduled.");
    }
}
  • schedule(TimerTask task, long delay, long period)
  • schedule(TimerTask task, Date time, long period)
  • scheduleAtFixedRate(TimerTask task, long delay, long period)
  • scheduleAtFixedRate(TimerTask task, Date firstTime, long period)
当计划反复执行的任务时,如果你注重任务执行的平滑度,那么请使用schedule方法,如果你在乎的是任务的执行频度那么使用scheduleAtFixedRate方法。 例如,这里使用了schedule方法,这就意味着所有beep之间的时间间隔至少为1秒,也就是说,如果有一个beap因为某种原因迟到了(未按计划执行),那么余下的所有beep都要延时执行。如果我们想让这个程序正好在3秒以后终止,无论哪一个beep因为什么原因被延时,那么我们需要使用scheduleAtFixedRate方法,这样当第一个beep迟到时,那么后面的beep就会以最快的速度紧密执行(最大限度的压缩间隔时间)。

进一步分析schedule和scheduleAtFixedRate

(1)2个参数的schedule在制定任务计划时, 如果指定的计划执行时间scheduledExecutionTime<=systemCurrentTime,则task会被立即执行。scheduledExecutionTime不会因为某一个task的过度执行而改变。
(2)3个参数的schedule在制定反复执行一个task的计划时,每一次执行这个task的计划执行时间随着前一次的实际执行时间而变,也就是scheduledExecutionTime(第n+1次)=realExecutionTime(第n次)+periodTime。也就是说如果第n次执行task时,由于某种原因这次执行时间过长,执行完后的systemCurrentTime>=scheduledExecutionTime(第n+1次),则此时不做时隔等待,立即执行第n+1次task,而接下来的第n+2次task的scheduledExecutionTime(第n+2次)就随着变成了realExecutionTime(第n+1次)+periodTime。说白了,这个方法更注重保持间隔时间的稳定。
(3)3个参数的scheduleAtFixedRate在制定反复执行一个task的计划时,每一次执行这个task的计划执行时间在最初就被定下来了,也就是scheduledExecutionTime(第n次)=firstExecuteTime+nperiodTime;如果第n次执行task时,由于某种原因这次执行时间过长,执行完后的systemCurrentTime>=scheduledExecutionTime(第n+1次),则此时不做period间隔等待,立即执行第n+1次task,而接下来的第n+2次的task的scheduledExecutionTime(第n+2次)依然还是firstExecuteTime+(n+2)periodTime这在第一次执行task就定下来了。说白了,这个方法更注重保持执行频率的稳定。
http://blog.csdn.net/gtuu0123/article/details/6040159
  1. schedule方法:“fixed-delay”;如果第一次执行时间被delay了,随后的执行时间按 照 上一次 实际执行完成的时间点 进行计算
  2. scheduleAtFixedRate方法:“fixed-rate”;如果第一次执行时间被delay了,随后的执行时间按照 上一次开始的 时间点 进行计算,并且为了”catch up”会多次执行任务,TimerTask中的执行体需要考虑同步
  3. schedule方法:下一次执行时间相对于 上一次 实际执行完成的时间点 ,因此执行时间会不断延后
  4. scheduleAtFixedRate方法:下一次执行时间相对于上一次开始的 时间点 ,因此执行时间不会延后,存在并发性

一些注意的问题

每一个Timer仅对应唯一一个线程。 Timer不保证任务执行的十分精确。 Timer类的线程安全的。 参考
void
cancel() Cancels the Timer and all scheduled tasks.
int purge() Removes all canceled tasks from the task queue.
void schedule(TimerTask task, Date when, long period) Schedule a task for repeated fixed-delay execution after a specific time has been reached. 直到when设定的时间开始,间隔period时间执行一次task,间隔时间是从task完成之后开始计时
void schedule(TimerTask task, long delay, long period) Schedule a task for repeated fixed-delay execution after a specific delay. 等待delay时间,每间隔period时间执行task
void
schedule(TimerTask task, Date when) Schedule a task for single execution. 直到when执行一次task
void
schedule(TimerTask task, long delay) Schedule a task for single execution after a specified delay. 等待delay时间执行一次task
void
scheduleAtFixedRate(TimerTask task, long delay, long period) Schedule a task for repeated fixed-rate execution after a specific delay has passed. 等待delay设定时间,间隔period时间执行一次task,间隔时间是从task开始之后计时,可能task完成需要的时间超过period的时间,则有可能同时有多个task在执行。
void
scheduleAtFixedRate(TimerTask task, Date when, long period) Schedule a task for repeated fixed-rate execution after a specific time has been reached. 直到when设定的时间开始,间隔period时间执行一次task,间隔时间是从task开始之后计时
public class TimeThread extends Thread {

 Handler handler = new Handler(){

  @Override
  public void handleMessage(Message msg) {
   // TODO Auto-generated method stub
   super.handleMessage(msg);
   switch (msg.what) {
   case 1:
    long sysTime = System.currentTimeMillis();
    CharSequence sysTimeStr = DateFormat.format("hh:mm:ss", sysTime);
    tv.setText(sysTimeStr);
    break;

   default:
    break;
   }
  }
 };
 
 @Override
 public void run() {
  do {
   try {
    Thread.sleep(1000);
    Message msg = new Message();
    msg.what = 1;
    handler.sendMessage(msg);
    
   } catch (InterruptedException e) {
    e.printStackTrace();
   }
   
  } while (true);
 }
}

Related Articles

Quote Of The Day