一 Timer 介绍
在开发中我们经常会遇到一些简单定时任务的需求,而不需要量级较重的定时任务就可以采取java定时器;
java.util.Timer工具类中的Timer 是定时器,但定时任务写在java.util.TimerTask 中,由 Timer 执行 TimerTask ;
Timer 的本质就是线程,构造方法如下
public Timer(String name) {
thread.setName(name);
thread.start();
}
从源码角度可知,如果这样创建定时器非守护线程,即使主线程运行结束,定时任务还是会执行;如果我们会使用如下的构造方式创建定时任务就是守护线程方式,会随着主线程的消亡而消亡;
public Timer(String name, boolean isDaemon) {
thread.setName(name);
thread.setDaemon(isDaemon);
thread.start();
}
Timer内部维护了一个优先队列,用于顺序执行TimerTask任务;
private final TaskQueue queue = new TaskQueue();
private final TimerThread thread = new TimerThread(queue);
优先队列的实现就是数组方式作为平衡二叉堆
private TimerTask[] queue = new TimerTask[128];
TimerTask 实现了Runnable 接口,执行的任务动作就是run方法;
public abstract class TimerTask implements Runnable {
// ....
protected TimerTask() {
}
public abstract void run();
//....
}
所以定时器的本质就是启动了一个新的线程执行任务,这些任务都会维护在优先队列里面;
timer的调度主要方法如下
- schedule(TimerTask task, long delay) 延迟 delay 毫秒执行一次
- schedule(TimerTask task, Date time) 指定时间执行一次
- schedule(TimerTask task, long delay, long period) 延迟delay毫秒以后,每隔period毫秒执行一次
- schedule(TimerTask task, Date firstTime, long period) 从firstTime时刻开始,每隔period毫秒执行一次
二 Timer使用示例
如果方式简单使用Timer去延迟执行线程任务,当然其它四种方法同理,调用方式区别不大;
public static void main(String[] args) {
// 创建定时器
Timer timer = new Timer("知识追寻者");
// 创建定时器任务;实现run 方法
TimerTask timerTask = new TimerTask() {
@Override
public void run() {
System.out.println("线程执行了"+ LocalTime.now());
}
};
System.out.println("程序执行时间"+ LocalTime.now());
// 启动定时器
timer.schedule(timerTask,5000);
}
输出的间隔大约就是5秒
程序执行时间 18:47:28.258
线程执行了18:47:33.259
三 cancel 方法
TimerTask的cancel () 方法是将自身任务从任务队列中移除
我们先延迟2秒,再每2秒执行一次看效果
public static void main(String[] args) {
// 创建定时器
Timer timer = new Timer("知识追寻者");
// 创建定时器任务;实现run 方法
TimerTask timerTask = new TimerTask() {
@Override
public void run() {
System.out.println("线程执行了"+ LocalTime.now());
}
};
System.out.println("程序执行时间"+ LocalTime.now());
// 启动定时器
timer.schedule(timerTask,2000,2000);
}
结果是每2秒会执行一次
程序执行时间18:59:23.557
线程执行了18:59:25.558
线程执行了18:59:27.558
线程执行了18:59:29.559
启用cancel方法,后 就只会执行一次
public static void main(String[] args) {
// 创建定时器
Timer timer = new Timer("知识追寻者");
// 创建定时器任务;实现run 方法
TimerTask timerTask = new TimerTask() {
@Override
public void run() {
System.out.println("线程执行了"+ LocalTime.now());
// 调用 cancel方法
this.cancel();
}
};
System.out.println("程序执行时间"+ LocalTime.now());
// 启动定时器
timer.schedule(timerTask,2000,2000);
}
输出如下
程序执行时间19:02:12.770
线程执行了19:02:14.772
Timer的cancel方法是移除所有的任务;
public static void main(String[] args) {
// 创建定时器
Timer timer = new Timer("知识追寻者");
// 创建定时器任务;实现run 方法
TimerTask timerTask = new TimerTask() {
@Override
public void run() {
System.out.println("线程执行了"+ LocalTime.now());
}
};
System.out.println("程序执行时间"+ LocalTime.now());
timer.cancel();
// 启动定时器
timer.schedule(timerTask,2000,2000);
}
输出直接报错
程序执行时间:19:34.294
Exception in thread "main" java.lang.IllegalStateException: Timer already cancelled.
at java.util.Timer.sched(Timer.java:397)
at java.util.Timer.schedule(Timer.java:248)
at com.youku1327.base.timer.TimerAbsolute.main(TimerAbsolute.java:28)
四 scheduleAtFixedRate
schedule 与 scheduleAtFixedRate 方法的区别如下:
schedule 方法如果执行任务的时间没有被延迟,下一次执行任务时间参考的是上一次任务执行的开始时间
scheduleAtFixedRate 方法如果执行任务的时间没有被延迟,下一次执行任务时间参考的是上一次任务执行的结束时间
五 Timer缺点
- Timer 对调度的支持是基于绝对时间的,而不是相对时间,所以它对系统时间的改变非常敏感。
- Timer 线程是不会捕获异常的,如果 TimerTask 抛出的了未检查异常则会导致 Timer 线程终止。
关注公众号:回复 拍拍知识追寻者,领取面试资料和原创PDF教程;