Java Timer使用介绍( 三 )


通过以上测试对比,我们可以感受到Timer中固定速率和固定延时的区别,但为了避免出错,使用Timer时应让TimerTask耗时尽可能短 。
4. 其他要点

  1. 以上是仅第3次任务加上了耗时11秒,如果是所有任务都耗时11秒呢?
如果每次任务执行都耗时11秒,那么无论是固定速率还是固定延时,都将是11秒执行一个任务 。
  1. 如果改为schedule(TimerTask task, Date firstTime, long period)和scheduleAtFixedRate(TimerTask task, Date firstTime, long period)来调度任务,firstTime指定为10点 , 而当前系统时间为11点,会出现什么情况呢?
虽然firstTime已经过期,但是Timer将会立即开始执行任务 , 之后按照period间隔重复执行任务 。
  1. 如果TimerTask执行过程中抛出了异常会发生什么事情?
Timer内部仅维护一个线程,当任一TimerTask抛出异常,将导致此线程终止运行,该Timer负责的所有任务都无法执行 。
四、调度多个TimerTask在上一节中,介绍的是一个可重复执行的TimeTask,如果执行耗时大于设定的间隔period , 将会影响该TimerTask下一次执行的时间点 。
而这一节则是为了单独说明,一个Timer同时调度多个TimeTask也会互相影响 。
示例:
TimerTask task1 = new TimerTask() {private int i = 1;@Overridepublic void run() {System.out.print(i + " task1:" + DateUtil.formatNow() + " 开始执行, ");ThreadUtil.sleep(11 * 1000);System.out.println(DateUtil.formatNow() + " 结束");i++;}};TimerTask task2 = new TimerTask() {private int i = 1;@Overridepublic void run() {System.out.print(i + "task2:" + DateUtil.formatNow() + " 开始执行, ");ThreadUtil.sleep(11 * 1000);System.out.println(DateUtil.formatNow() + " 结束");i++;}};Timer timer = new Timer("timer");timer.scheduleAtFixedRate(task1, 5000, 2000);timer.scheduleAtFixedRate(task2, 5000, 2000);输出:
1 task1:2022-10-31 16:58:27 开始执行, 2022-10-31 16:58:38 结束1 task2:2022-10-31 16:58:38 开始执行, 2022-10-31 16:58:49 结束2 task2:2022-10-31 16:58:49 开始执行, 2022-10-31 16:59:00 结束2 task1:2022-10-31 16:59:00 开始执行, 2022-10-31 16:59:11 结束3 task1:2022-10-31 16:59:11 开始执行, 2022-10-31 16:59:22 结束3 task2:2022-10-31 16:59:22 开始执行, 2022-10-31 16:59:33 结束4 task2:2022-10-31 16:59:33 开始执行, 2022-10-31 16:59:44 结束4 task1:2022-10-31 16:59:44 开始执行, 2022-10-31 16:59:55 结束可以发现 , task1和task2其实都没有按照既定时间去执行任务了 。
根本原因是在于,Timer内部仅维护一个线程执行所有TimerTask,为了避免错误 , 一个Timer对象最好仅调度一个TimerTask对象,除非可以确保多个TimerTask之间一定不会相互影响 。
因此编写TimerTask时应当自行捕获异常 。
五、取消任务Timer在创建时实际上是默认在内部维护了一个非守护线程,即使任务全部执行完成,线程也并不会销毁 。
Timer提供cancel()方法,可以手动调用取消定时器所有的任务,并销毁定时器 。
如果想要Timer内部创建的是守护线程,可以使用以下构造方法创建定时器,设置isDaemon为true:
  • Timer(boolean isDaemon)
  • Timer(String name, boolean isDaemon)
如果没有自己定义name参数,默认Timer内部自动命名为“Timer-递增序号”,作为内部线程的线程名称,在构造方法内启动此线程 。
如果是要取消单个任务 , 可以使用TimerTask的cancel()方法 。
当TimerTask调用cancel之后,任务是取消了,但Timer自身并不能马上知道TimerTask被取消,而是在准备执行前才知道,因此Timer内部还维护着这个任务的引用 。若希望Timer立即清除引用,可调用Timer.purge()立即执行清除 。

推荐阅读