• 拾遗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教程;

  • 相关阅读:
    TLS握手、中断恢复与证书中心的原因
    PROC 文件系统调节参数介绍(netstat -us)
    CPU状态信息us,sy,ni,id,wa,hi,si,st含义
    优化Linux内核参数
    linux内核(kernel)版本号的意义
    ethhdr、ether_header、iphdr、tcphdr、udphdr 结构介绍
    linux下将不同线程绑定到不同core和cpu上——pthread_setaffinity_np
    module_init的加载和释放
    (一)洞悉linux下的Netfilter&iptables:什么是Netfilter?
    netfilter的钩子——数据包在内核态得捕获、修改和转发
  • 原文地址:https://www.cnblogs.com/zszxz/p/13863136.html
Copyright © 2020-2023  润新知