定时计划任务功能在Java中主要使用的就是Timer对象,它在内部使用多线程的方式进行处理,所以它和其它线程技术上还是有非常大的关联的。
定时器Timer的使用
在JDK库中Timer类主要负责计划任务的功能,也就是在指定的时间开始执行某一个任务。Timer类的方法列表如图1所示。
Timer类的主要作用就是设置计划任务,但封装任务的类却是TimerTask类,类结构如图2所示。执行计划任务的代码要放入TimerTask的子类中,因为因为TimerTask是一个抽象类。
图1 类Timer的方法列表
图2 类TimerTask类相关的信息
方法schedule(TimerTask task,Date time)的测试
该方法的作用是在指定的日期执行一次某一任务。
1 执行任务的时间晚于当前的时间:在未来执行的效果
代码清单1 在未来执行的效果
程序运行后的结果如图3所示。任务虽然执行完了,但进程还未销毁,为什么会出现这样的情况?
图3 运行结果
在创建Timer对象时,JDK源码如下:
此构造方法调用的是如下构造方法:
查看构造方法可以得知,创建一个Timer就是启动一个新的线程,这个新启动的线程并不是守护线程,它一直在运行。
查看构造方法可以得知,创建一个Timer就是启动一个新的线程,这个新启动的线程并不是守护线程,它一直在运行。
代码清单2 Timer的守护线程
程序运行后迅速结束当前的进程,并且TimerTask中的任务不再被运行,因为线程已经结束了。
图4 守护线程创建成功进程退出
2 计划时间早于当前时间:提前运行的效果
如果执行任务的时间早于当前时间,则立即执行task任务。将代码清单2中//Timer timer=new Timer();
注释去掉。
图5 立即执行task任务
3 多个TimerTask任务及延时的测试
Timer中允许有多个TimerTask任务。
代码清单3 多个TimerTask任务
程序运行结果如图6所示。
图6 一个Timer中可以运行多个TimerTask
TimerTask是以队列的方式一个一个被顺序执行的,所以执行的时间有可能和预期的时间不一致,因为前面的任务有可能消耗的时间较长,则后面的任务运行的时间也会被延迟。
代码清单4 任务运行被延迟
由于task1需要用时20秒执行完成任务,task1开始的时间是2017-11-14 10:30:18,那么将要影响task2的计划任务的时间,task2以此时间为基准,向后延迟20秒,task2在11:33:20执行任务。因为Task是被放入队列中的,得一个一个顺序运行。
程序运行结果如图6所示。
图7 任务2的运行时间被延迟了
方法schedule(TimerTask task,Date time,long period)的测试
该方法的作用是指在指定日期之后,按指定的间隔周期性地无限循环执行某一任务。
1 计划时间晚于当前时间:在未来执行的效果。
代码清单5 在未来的时间开始循环执行
运行结果如图8所示,每隔4秒运行一次TimerTask,并且无限期地重复执行。
图8 运行结果
2 计划时间早于当前时间:提前运行的效果
如果计划时间早于当前时间,则立即执行task。
运行代码清单5中程序,运行结果如图9所示。
图9 立即执行task任务
3 任务执行时间被延迟时
代码清单6 任务执行时间被延迟
运行结果如图10所示,任务被延时了但还是一个一个顺序运行。
图10 运行结果
4 TimerTask类的cancel()方法
TimerTask类中的cancel()方法的作用是将自身从任何队列中清楚。
代码清单7 TimerTask类的cancel()方法
TimerTask类的cancel()方法是将自身从任务队列中被移除,其他任务不受影响。
图11 TimerTaskA仅运行一次后被清楚了
5 Timer类的cancel()方法
和TimerTask类中的cancel()方法清楚自身不同,Timer类中的cancel()方法的作用是将任务队列中的全部任务清空。
代码清单8 Timer类的cancel()方法
运行结果如图12所示,全部任务被清除,并且进程被销毁,按钮有红色变为灰色。
图12 进程被清空
6 Timer的cancel()方法注意事项
Timer类中的cancel()方法有时并不一定会停止执行计划任务,而是正常执行。
代码清单9 cancel不一定能停止执行计划
运行结果如图13所示。这是因为Timer类中的cancel()方法有时并没有争抢到queue锁,所以TimerTask 正常运行。
图13 并没有停止
方法schedule(TimerTask task,long delay)的测试
该方法的作用是以执行schedule(TimerTask task,long delay)方法当前的时间为参考时间,在此时间基础上延迟指定的毫秒数后执行一次TimerTask任务。
代码清单10 延迟执行一次
任务task被延迟7秒执行,如图14所示。
图14 运行结果
方法schedule(TimerTask task,long delay,long period)的测试
该方法的作用是以执行schedule(TimerTask task,long delay,long period)方法当前的时间Wie参考时间,在此时间基础上延迟指定的毫秒数,再以某一间隔时间无限次数地执行某一任务。
代码清单11 无限循环
凡是使用方法带有period参数的,都是无限循环执行TimerTask中的任务。
图15 循环运行
方法scheduleAtFixedRate(TimerTask task,Date firstTime,long period)的测试
方法schedule和方法scheduleAtFixedRate都会按顺序执行,所以不要考虑非线程安全的情况。
方法schedule和scheduleAtFixedRate主要的区别只在于不延时的情况。
使用schedule方法:如果执行任务的时间没有被延迟时,那么下一次任务的执行时间参考的是上一次任务的“开始”时的时间来计算。
使用scheduleAtFixedRate方法:如果执行任务的时间没有被延迟时,那么下一次任务的执行时间参考的是上一次任务的“结束”时的时间来计算。
延时的情况则没有区别,也就是使用schedule或scheduleAtFixedRate方法都是如果执行任务的时间被延迟时,那么下一次任务的执行时间参考的是上一次任务“结束”时的时间来计算。
1 测试schedule方法任务不延时
代码清单12 测试schedule方法任务不延时
控制台打印的结果证明,在不延时的情况下,如果执行任务的时间没有被延迟时,则下一次执行任务的时间是上一次任务的开始时间加上delay时间。
图16 没有延时的运行效果
2 测试schedule方法任务超时
代码清单13 schedule方法任务超时
程序运行结果如图17所示,从控制台打印的结果来看,如果执行任务的时间被延迟时,那么下一次的执行时间以上一次“结束”时的时间为参数来计算。
图17 任务延时的效果
3 测试scheduleAtFixedRate方法任务不延时
代码清单14 scheduleAtFixedRate方法任务不延时
运行结果如图18所示,控制台打印的结果证明,如果执行任务的时间没有被延迟,则下一次执行任务的时间是上一次任务的开始时间加上delay时间。
图18 没有被延时的运行效果
4 测试scheduleAtFixedRate方法任务超时
代码清单15 scheduleAtFixedRate方法任务超时
运行结果如图19所示,如果执行任务的时间被延迟,那么下一次任务的执行时间以上一次任务“结束”时的时间为参考来计算。
图19 任务延时的运行效果
5 验证schedule方法不具有追赶执行性
代码清单16 schedule方法不具有追赶执行性
运行结果如图20所示,时间“2017-11-14 15:13:43”到“2017-11-14 15:16:06”之间的时间所对应的Task任务被取消了,不执行了。这就是Task不追赶的情况。
图20 不追赶的情况
6 验证scheduleAtFixedRate方法具有追赶执行性
代码清单17 scheduleAtFixedRate方法具有追赶执行性
运行结果如图21所示,两个时间段内所对应的Task被“补充性”执行了,这就是Task任务追赶执行的特性。
图21 追赶的情况