拾遗Timer定时器

一 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教程;

posted @ 2020-10-23 11:23  知识追寻者  阅读(168)  评论(0编辑  收藏  举报