Java中的Timer和TimerTask简介

Java中的Timer和TimerTask简介


概述


在Java中,Timer和TimerTask是两个经常用来实现定时器的类。这两个类使用起来非常方便,可以完成我们对定时器的绝大多数需求。
Timer是一种定时器工具,用来在一个后台线程计划执行指定任务,并可以按计划一次或反复多次执行一个任务。它是用来执行任务的类,接收一个TimerTask实例作为参数。通常,Timer类有两种执行任务的模式。最常用的是schedule模式,它可以通过两种方式执行任务:在某个特定时刻开始执行任务;从当前时刻开始,在某个固定时段之后开始执行任务。这两种方式都可以指定任务重复执行的频率。另一种执行任务的模式是scheduleAtFixedRate。在这种模式下,Timer类会尽量使任务保持在一个固定的频率下重复执行。
TimerTask一个抽象类,它的子类代表一个可以被Timer计划的任务。
下面先看一个简单的例子。
例1:

import java.util.Timer;
import java.util.TimerTask;

public class Reminder {
Timer timer;

class ReminderTask extends TimerTask {
public void run() {
print("Welcome!");
timer.cancel();
}
}

public Reminder(int seconds) {
timer = new Timer();
timer.schedule(new ReminderTask(), seconds * 1000);
}

private static void print(String s) {
System.out.println(s);
}

public static void main(String args[]) {
print("Hello!");
new Reminder(3);
print("Good!");
}
}

运行程序,首先输出
Hello!
Good!

三秒钟后输出
Welcome!

例1说明用Timer线程实现和按计划执行一个任务的一般步骤:
[1] 实现自定义的TimerTask的子类及其run方法,其中run方法包含要执行的任务代码。
[2] 实例化Timer类,创建计时器后台线程。
[3] 实例化任务对象,如new RemindTask()。
[4] 制定执行计划。这里用schedule方法,第一个参数是TimerTask对象,第二个参数表示开始执行任务前的延时时间(单位是毫秒milliseconds)。还有另一种方法可以指定任务的执行时间,如例2,指定任务在03:30:45 p.m.执行。
例2:

import java.util.Calendar;
import java.util.Date;
import java.util.Timer;
import java.util.TimerTask;

public class Reminder {
Timer timer;

class ReminderTask extends TimerTask {
@Override
public void run() {
print("Welcome!");
timer.cancel();
}
}

public Reminder(Date time) {
timer = new Timer();
timer.schedule(new ReminderTask(), time);
}

private static void print(String s) {
System.out.println(s);
}

public static void main(String args[]) {
Calendar calender = Calendar.getInstance();
calendar.set(Calendar.HOUR_OF_DAY, 15);
calendar.set(Calendar.MINUTE, 30);
calendar.set(Calendar.SECOND, 45);
Date time = calendar.getTime();

print("Hello!");
new Reminder(time);
print("Good!");
}
}

运行程序,首先输出
Hello!
Good!

到03:30:45 p.m.时输出
Welcome!

终止Timer线程


默认情况下,只要一个程序的Timer线程在运行,那么这个程序就会保持运行。如果需要终止Timer线程,一般可以通过以下四种方法:
[1] 调用Timer的cancel方法。可以从程序的任何地方调用此方法,甚至可以在一个TimerTask的run方法里调用。
[2] 让Timer线程成为一个Daemon线程(可以在创建Timer时使用new Timer(true)达到这个目地),这样当程序只有Daemon线程的时候,它就会自动终止运行。
[3] 当Timer相关的所有task执行完毕以后,删除所有此Timer对象的引用(置成null),这样Timer线程也会终止。
[4] 调用System.exit方法,使整个程序(包括其所有线程)终止。

在Reminder的例子中,使用第一种方法终止线程,但不能使用第二种方式。因为Reminder需要程序保持运行直到Timer的任务执行完成,如果设成 Daemon,那么当main线程结束的时候,程序只剩下Timer这个Daemon线程,于是程序不会等Timer线程执行task就终止了。


重复执行任务


如果需要反复执行某一任务,可以使用三个参数的schedule方法,指定task每隔一段时间执行一次。
例3:

import java.util.Timer;
import java.util.TimerTask;

public class Reminder {
Timer timer;
int times;

class ReminderTask extends TimerTask {
@Override
public void run() {
if (times > 0) {
print("Welcome!");
times--;
} else {
timer.cancel();
}
}
}

public Reminder(int delayScd, int repeatScd, int repeatTimes) {
this.times = repeatTimes;
timer = new Timer();
timer.schedule(new ReminderTask(), delayScd*1000, repeatScd*1000);
}

private static void print(String s) {
System.out.println(s);
}

public static void main(String args[]) {
print("Hello!");
new Reminder(5, 3, 4);
print("Good!");
}
}

运行程序,首先输出
Hello!
Good!

然后每隔,三秒钟后输出一次,一共输出四次
Welcome!
Welcome!
Welcome!
Welcome!

例3中使用了三个参数的schedule方法用来指定task每隔三秒执行一次。
下面列为所有Timer类用来制定计划反复执行task的方法 :
[1] schedule(TimerTask task, long delay, long period)
[2] schedule(TimerTask task, Date time, long period)
[3] scheduleAtFixedRate(TimerTask task, long delay, long period)
[4] scheduleAtFixedRate(TimerTask task, Date firstTime, long period)
当计划反复执行的任务时,如果注重任务执行的平滑度,那么可以使用schedule方法;如果注重任务的执行频度,可以使用 scheduleAtFixedRate方法。

例如,例3中使用了schedule方法,这就意味着所有print方法之间的时间间隔至少为三秒。也就是说,如果有一个print方法因为某种原因迟到了(未按计划执行),那么余下的所有print方法都要延时执行。如果想让这个程序正好在三秒以后终止,无论哪一个print方法因为什么原因被延时,那么我们需要使用 scheduleAtFixedRate方法。这样当第一个print迟到时,那么后面的print就会以最快的速度紧密执行(最大限度的压缩间隔时间)。


schedule和scheduleAtFixedRate


包含两个参数的schedule在制定任务计划时,如果指定的计划执行时间scheduledExecutionTime <= systemCurrentTime,则task会被立即执行。scheduledExecutionTime不会因为某一个task的过度执行而改变。
包含三个参数的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。因此,这个方法更注重保持间隔时间的稳定。

包含三个参数的scheduleAtFixedRate 在制定反复执行一个task的计划时,每一次执行这个task的计划执行时间在最初就被定下来了,也就是scheduledExecutionTime(第n次) = firstExecuteTime + n*periodTime;如果第n次执行task时,由于某种原因这次执行时间过长,执行完后的systemCurrentTime >= scheduledExecutionTime(第n+1次),则此时不需要period间隔等待,立即执行第n+1次task,而接下来的第n+2次的task的scheduledExecutionTime(第n+2次)依然还是firstExecuteTime+(n+2)*periodTime这在第一次执行task就定下来了。因此,这个方法更注重保持执行频率的稳定。


需要注意的问题


[1] 每个Timer会单独开启一个线程。
[2] 每个TimerTask只能被schedule一次。
[3] Timer一旦取消,它的线程也被注销,即不能再调用Timer的schedule函数。
[4] Timer不保证任务执行的十分精确。
[5] Timer类的线程安全的。
  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值