java.util.Timer类的作者是大名鼎鼎的Josh Bloch,他可是Java集合框架的作者,谷歌首席架构师。最近一直想研究定时任务,所以就从最最古老的类开始使用吧。
从API文档里看到一个延迟的方法,就是延迟执行任务。我这里的每个任务都是打印任务名称。但是延迟的时间从5秒递减到1秒。为Timer中添加5个任务。运行结果是什么呢?
public static void main(String[] args) {
Timer timer = new Timer();
timer.schedule(getRandomTask(5), 5000);
timer.schedule(getRandomTask(4), 4000);
timer.schedule(getRandomTask(3), 3000);
timer.schedule(getRandomTask(2), 2000);
timer.schedule(getRandomTask(1), 1000);
}
private static TimerTask getRandomTask(int num) {
TimerTask task = new TimerTask() {
@Override
public void run() {
System.out.println("任务" + num);
}
};
return task;
}
从运行结果来看,看来Timer类内部很可能维护了一个"任务队列",并且知道这个任务在具体得某一刻开始执行。延迟最小的任务1只延迟了1秒,所以第一个打印出来。相反,任务5虽然在主函数的第一行执行,但是延迟5秒后才最后打印出来。所以可以确定,一个Timer对象能够执行多个任务。
拨云见日
Timer类能支持执行多个任务,并且是按照顺序执行。
下面的代码几乎与上面得相同,只是在构造任务的时候加了一个限制,就是说如果执行了任务3会出错。运行结果又会是什么呢?
public static void main(String[] args) {
Timer timer = new Timer();
timer.schedule(getRandomTask(5), 5000);
timer.schedule(getRandomTask(4), 4000);
timer.schedule(getRandomTask(3), 3000);
timer.schedule(getRandomTask(2), 2000);
timer.schedule(getRandomTask(1), 1000);
}
private static TimerTask getRandomTask(int num) {
TimerTask task = new TimerTask() {
@Override
public void run() {
System.out.println("任务" + num);
if (num == 3) {
String error = "任务" + num + "报错";
throw new RuntimeException(error);
}
}
};
return task;
}
我把本次运行结果与上次运行结果拿出来对比一下。任务3报错,实际上就会让本该执行的任务4与任务5执行不了。换句话说,Timer类在多线程并行处理任务的时候,只要其中一个定时任务出错,那么其它任务也都会自动停止运行。这在生产库上是绝对不允许的。或许这也是Timer被废弃的重要缺陷之一吧。
本次程序运行结果: |
上次程序运行结果 |
拨云见日
Timer类在执行多个任务的时候,如果其中一个任务出错,并且没有捕获异常的话,其它任务也会终止执行。所以为了防止这种情况的出现,需要为每个任务使用try--catch语句。
如果某个定时任务出现了死循环,那么会对其它定时任务造成影响吗?
public static void main(String[] args) {
Timer timer = new Timer();
timer.schedule(getRandomTask(5), 5000);
timer.schedule(getRandomTask(4), 4000);
timer.schedule(getRandomTask(3), 3000);
timer.schedule(getRandomTask(2), 2000);
timer.schedule(getRandomTask(1), 1000);
}
private static TimerTask getRandomTask(int num) {
return new TimerTask() {
@Override
public void run() {
System.out.println("任务" + num);
//死循环
while (true) {
}
}
};
}
拨云见日
Timer中允许有多个TimerTask任务,并且是按照顺序执行的,不能同时执行两个任务。
任务如果前一个时间执行过长,很可能导致后面的任务延时执行。
如果前一个任务出现阻塞,可能造成后面的任务都得不到执行,导致死锁。
(1)如果只执行一次的定时任务错过执行,Timer会怎么样呢?
-- 运行时间: 17:55:00 分
timer.schedule(getRandomTask(2), todayAt(17,38,0));
从结果来看,schedule方法为"只执行一次的定时任务,并且这个任务已经过了应该执行的时间",Timer调度会追加执行一次。
(2)如果是按照固定频率执行的定时任务会怎么样?
-- 运行时间18:03:00,从18点开始,每30秒执行一次。
timer.scheduleAtFixedRate(getRandomTask(1), todayAt(18, 0, 0), 30000);
从结果来看,scheduleAtFixedRate方法具有追赶性。如果计划时间在过去,那么从过去到现在这段时间间隔内的任务也会周期的全部追加执行。
(3)另外一个按照固定频率执行的定时任务
-- 运行时间 18:06
timer.scheduleAtFixedRate(getRandomTask(1), todayAt(18, 0, 0), 30000);
运行结果:如果有错过的任务,那么在启动的时候立即执行一次,然后以当前时间为开始时间,按照固定得频率执行定时任务。
拨云见日
Timer类对于只执行一次的定时任务,如果任务执行时间错过了,那么在启动的时候,会追加执行一次。
Timer类对于按照固定频率执行的定时任务
scheduleAtFixedRate方法的MissFire策略是 追加执行所有错过的任务
schedule方法的MissFire策略是立即执行一次,并以当前时间为基准,按照固定的频率执行任务