一、周期性任务实现
1.1 应用
在实际应用中,我们需要创建一些延迟性的、周期性的任务,如每隔一小时记录一次日志、每隔一小时统计一次数据等。在 java 中给我们提供了两个实现方式,分别为 Timer 和 ScheduledThreadPoolExecutor。
1.2 对比
1.2.1 两者对比
Timer | ScheduledThreadPoolExecutor | |
线程 | 只支持单线程 | 支持多线程 |
多任务 | 任务之间互相影响 | 任务直接隔离 |
调度时间 | 绝对时间(受系统时间影响) | 相对时间 |
异常 | 单任务异常,则后续任务受影响 | 无影响 |
支持版本 | > JDK1.3 | > JDK1.5 |
推荐度 | 不推荐 | 推荐使用 |
1.2.2 Timer 存在的问题
● Timer的问题:依赖系统时间,系统时间变化影响精度
Timer 类的调度是基于系统时间的,而不是相对时间,因为 Timer 类对系统时钟的变化是敏感的。例如,设定了一个每隔 10 秒执行一次的任务,在某个时刻,系统时间提前了5秒,则下一个任务就会在5秒后执行,而不是10秒后从而影响到后续的所有任务执行
● ScheduledThreadPoolExecutor 如何解决
ScheduledThreadPoolExecutor 中,任务调度是基于相对时间的。它在任务的内部存储了该任务距离下次调度还需要的时间(使用的是基于 System#nanoTime实现的相对时间 ,不会因为系统时间改变而改变,如距离下次执行还有10秒,不会因为将系统时间调前6秒而变成4秒后执行)
二、实现方式
2.1 Timer 实现
public class TestTimer {
public static void main(String[] args) {
String time = new SimpleDateFormat("HH:mm:ss").format(new Date());
System.out.println("Start time : " + time);
Timer timer = new Timer();
TimerTask task = new TimerTask() {
@Override
public void run() {
// TODO Auto-generated method stub
String time = new SimpleDateFormat("HH:mm:ss").format(new Date());
System.out.println("Now Time : " + time);
}
};
// 定义方法在多少秒后第一次被执行
int delay = 1000;
// 定义方法每次调用的间隔时间
int period = 5000;
timer.schedule(task, delay, period);
}
}
2.2 ScheduledThreadPoolExecutor 实现
public class TestScheduledThreadPoolExecutor {
public static void main(String[] args) {
String time = new SimpleDateFormat("HH:mm:ss").format(new Date());
System.out.println("Start time : " + time);
ScheduledThreadPoolExecutor executor = new ScheduledThreadPoolExecutor(5); //创建5个执行线程
Runnable runnable = new Runnable() {
@Override
public void run() {
// TODO Auto-generated method stub
String time = new SimpleDateFormat("HH:mm:ss").format(new Date());
System.out.println("Now Time : " + time);
}
};
executor.scheduleWithFixedDelay(runnable, 2, 3, TimeUnit.SECONDS);
}
}