Java 定时器:java.util.Timer 和 java.util.TimerTask

本文内容大多基于官方文档和网上前辈经验总结,经过个人实践加以整理积累,仅供参考。


JDK 1.3 开始引入定时器 TimerTimerTask,线程在后台安排一个未来执行的任务,这个任务可以只执行一次或按照固定的时间间隔重复执行。

1 TimerTask

TimerTask 是一个抽象类,由 Timer 安排为执行一次或重复执行的任务,主要方法:

1.1 run():用于执行定时任务,每一个具体的任务类都必须继承 TimerTask 并重写 run() 方法

1.2 cancel():取消定时器任务

1.3 scheduledExecutionTime():返回定时任务最近执行时间

2 Timer

Timer 类是线程安全的,保证多线程共享单个 Timer 对象而无需进行外部同步。

2.1 Timer 类通过 schedule 方法安排定时任务,schedule 方法有 4 个重载方法:

public void schedule(TimerTask task, long delay)

public void schedule(TimerTask task, Date time)

public void schedule(TimerTask task, long delay, long period)

public void schedule(TimerTask task, Date firstTime, long period)

2.1.1 在指定延迟(单位:毫秒)后执行定时任务

public static void main(String[] args) {
    Timer timer = new Timer();
    long start = System.currentTimeMillis();
    timer.schedule(new TimerTask() {
        @Override
        public void run() {
            long end = System.currentTimeMillis();
            System.out.println("Time Interval : " + (end - start));
        }
    }, 5000);
}

运行结果:

这里写图片描述

2.1.2 在指定时间执行指定任务

public static void main(String[] args) throws ParseException {
    String dateTime = "20170121123145";
    Calendar calendar = Calendar.getInstance();
    calendar.setTime(new SimpleDateFormat("yyyyMMddHHmmss").parse(dateTime));
    Timer timer = new Timer();
    timer.schedule(new TimerTask() {
        @Override
        public void run() {
            System.out.println(new Date());
        }
    }, new Date(calendar.getTimeInMillis()));
}

运行结果:

这里写图片描述

注意:如果代码运行的时间已经超过设置的定时时间,则定时任务会立即执行

public static void main(String[] args) throws ParseException {
    String dateTime = "20161231235959";
    Calendar calendar = Calendar.getInstance();
    calendar.setTime(new SimpleDateFormat("yyyyMMddHHmmss").parse(dateTime));
    Timer timer = new Timer();
    timer.schedule(new TimerTask() {
        @Override
        public void run() {
            System.out.println(new Date());
        }
    }, new Date(calendar.getTimeInMillis()));
}

运行结果:

这里写图片描述

2.1.3 在指定延迟(单位:毫秒)后按照固定时间间隔(单位:毫秒)重复执行定时任务

private static long start = 0;
public static void main(String[] args) {
    Timer timer = new Timer();
    start = System.currentTimeMillis();
    timer.schedule(new TimerTask() {
        @Override
        public void run() {
            long now = System.currentTimeMillis();
            System.out.println(now - start);
            start = now;
        }
    }, 10000, 5000);
}

运行结果:

这里写图片描述

从运行结果可以看出,除第一次是延迟 10 秒左右执行定时任务外,后续都是延迟 5 秒左右执行一次定时任务。

2.1.4 在指定时间开始按照固定时间间隔(单位:毫秒)重复执行定时任务

public static void main(String[] args) throws ParseException {
    String dateTime = "20170121124610";
    Calendar calendar = Calendar.getInstance();
    calendar.setTime(new SimpleDateFormat("yyyyMMddHHmmss").parse(dateTime));
    Timer timer = new Timer();
    timer.schedule(new TimerTask() {
        @Override
        public void run() {
            System.out.println(new Date() + " -- " + System.currentTimeMillis());
        }
    }, new Date(calendar.getTimeInMillis()), 5000);
}

运行结果:

这里写图片描述

同样,如果代码运行的时间已经超过设置的定时时间,则定时任务会立即执行

public static void main(String[] args) throws ParseException {
    String dateTime = "20161231235959";
    Calendar calendar = Calendar.getInstance();
    calendar.setTime(new SimpleDateFormat("yyyyMMddHHmmss").parse(dateTime));
    Timer timer = new Timer();
    timer.schedule(new TimerTask() {
        @Override
        public void run() {
            System.out.println(new Date() + " -- " + System.currentTimeMillis());
        }
    }, new Date(calendar.getTimeInMillis()), 5000);
}

运行结果:

这里写图片描述

2.2 Timer 类的 scheduleAtFixedRate 方法也用于安排定时任务,有 2 个重载方法:

public void scheduleAtFixedRate(TimerTask task, long delay, long period)

public void scheduleAtFixedRate(TimerTask task, Date firstTime, long period)

2.2.1 安排指定任务在指定延迟后开始按照固定速率执行定时任务

private static long start = 0;
public static void main(String[] args) {
    Timer timer = new Timer();
    start = System.currentTimeMillis();
    timer.scheduleAtFixedRate(new TimerTask() {
        @Override
        public void run() {
            long now = System.currentTimeMillis();
            System.out.println(now - start);
            start = now;
        }
    }, 10000, 5000);
}

运行结果:

这里写图片描述

注意:以固定速率执行定时任务,每一次任务执行时间与上一次任务执行时长相关,举个例子,假设第一次任务延迟 10 秒执行,后续按照固定速率 5 秒执行一次定时任务,实际运行过程中第一次任务执行时长为 11 秒,则接下来间隔 4 秒就要执行第二次定时任务,保证所有任务执行下来平均间隔时间无限接近于 5 秒的固定速率。从示例代码运行结果也可以看出。

2.2.2 安排指定任务在指定时间开始按照固定速率执行定时任务

public static void main(String[] args) throws ParseException {
    String dateTime = "20170121131130";
    Calendar calendar = Calendar.getInstance();
    calendar.setTime(new SimpleDateFormat("yyyyMMddHHmmss").parse(dateTime));
    Timer timer = new Timer();
    timer.scheduleAtFixedRate(new TimerTask() {
        @Override
        public void run() {
            System.out.println(new Date() + " -- " + System.currentTimeMillis());
        }
    }, new Date(calendar.getTimeInMillis()), 5000);
}

运行结果:

这里写图片描述

注意:如果代码运行的时间已经超过设置的定时时间,则定时任务会立即执行,而且会补全所有应该执行的定时任务次数

public static void main(String[] args) throws ParseException {
    String dateTime = "20170121133600";
    Calendar calendar = Calendar.getInstance();
    calendar.setTime(new SimpleDateFormat("yyyyMMddHHmmss").parse(dateTime));
    long fixedDateTime = calendar.getTimeInMillis();
    int timeInterval = 5000;
    Timer timer = new Timer();
    System.out.println(new Date() + " -- " + System.currentTimeMillis());
    System.out.println(
        (System.currentTimeMillis() - calendar.getTimeInMillis()) / timeInterval);
    timer.scheduleAtFixedRate(new TimerTask() {
        @Override
        public void run() {
            System.out.println(new Date() + " -- " + System.currentTimeMillis());
        }
    }, new Date(fixedDateTime), timeInterval);
}

运行结果:

这里写图片描述

从运行结果可以看出,指定开始时间是“2017-01-21 13:36:00”,实际运行时间是“2017-01-21 13:36:25”,运行的时间已经超过设置的定时时间,且这之间的时间间隔可以运行 5 次定时任务,所以定时器立刻连续执行了 5 次定时任务。

2.3 每个 Timer 对象对应单个后台线程,顺序执行所有计时器任务。如果任务执行时间过长,会独占计时器任务执行线程,其后所有线程都必须等待它执行完毕,这样会延迟后续任务的执行。

public static void main(String[] args) {
    Timer timer = new Timer();
    long start = System.currentTimeMillis();
    timer.schedule(new TimerTask() {
        @Override
        public void run() {
            try {
                Thread.sleep(10000);
                System.out.println(new Date());
                System.out.println(
                    "Time Interval : " + (System.currentTimeMillis() - start));
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }, 5000, 3000);
}

运行结果:

这里写图片描述

2.4 TimerTask cancel() 示例

private static int count = 0;
public static void main(String[] args) {
    Timer timer = new Timer();
    timer.schedule(new TimerTask() {
        @Override
        public void run() {
            System.out.println(new Date());
            count++;
            if (count > 3) {
                if (cancel()) {
                    System.out.println("Cancel");
                } else {
                    System.out.println("Fail");
                }
            }
        }
    }, 5000, 3000);
}

运行结果:

这里写图片描述

2.5 TimerTask scheduledExecutionTime() 示例

public static void main(String[] args) {
    Timer timer = new Timer();
    timer.schedule(new TimerTask() {
        @Override
        public void run() {
            System.out.println(scheduledExecutionTime());
        }
    }, 5000, 3000);
}

运行结果:

这里写图片描述

3 Timer 的缺陷

3.1 Timer 定时基于绝对时间,不是相对时间,对系统时间改变很敏感

3.2 Timer 线程不捕获异常,如果 TimerTask 抛出未检查异常会导致 Timer 线程终止,Timer 也无法重新恢复线程的执行

public static void main(String[] args) {
    Timer timer = new Timer();
    timer.schedule(new TimerTask() {
        public void run() {
            System.out.println("This is normal timer!");
        }
    }, 3000, 1000);
    timer.schedule(new TimerTask() {
        @Override
        public void run() {
            System.out.println("This is exceptional timer!");
            throw new RuntimeException();
        }
    }, 2000, 2000);
}

运行结果:

这里写图片描述

从运行结果可以看出,一旦出现异常,所有的定时任务都会被取消且不会恢复。

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

又言又语

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值