java.util.Timer
和java.util.TimerTask
类(我将二者统称为Java的定时器框架)使得程序员可以轻松地安排简单的任务。(请注意,这些类在 J2ME 中也可用。)在 Java 2 SDK 标准版 1.3 版中引入此框架之前,开发人员必须编写自己的调度程序,这涉及处理线程和Object.wait()
方法的复杂性。但是,Java 定时器框架不够丰富,无法满足许多应用程序的调度需求。即使是需要每天同时重复的任务也不能直接使用Timer
进行调度,因为夏令时的来来往往会发生时间跳跃。
本文介绍了一个调度框架,它是对Timer
和TimerTask
的推广,允许更灵活的调度。该框架非常简单——它由两个类和一个接口组成——而且很容易学习。如果你习惯于使用 Java 计时器框架,那么你应该能够很快掌握调度框架。)
安排一次性任务
调度框架构建在 Java 计时器框架类之上。因此,在解释调度框架的使用方式和实现方式之前,我们将首先了解如何使用这些类进行调度。
想象一个鸡蛋计时器,它通过播放声音告诉您何时过去了数分钟(因此你的鸡蛋已煮熟)。清单 1 中的代码构成了用 Java 语言编写的简单的鸡蛋计时器的基础:
清单 1. EggTimer 类
package org.tiling.scheduling.examples;
import java.util.Timer;
import java.util.TimerTask;
public class EggTimer {
private final Timer timer = new Timer();
private final int minutes;
public EggTimer(int minutes) {
this.minutes = minutes;
}
public void start() {
timer.schedule(new TimerTask() {
public void run() {
playSound();
timer.cancel();
}
private void playSound() {
System.out.println("Your egg is ready!");
// Start a new thread to play a sound...
}
}, minutes ∗ 60 ∗ 1000);
}
public static void main(String[] args) {
EggTimer eggTimer = new EggTimer(2);
eggTimer.start();
}
}
一个EggTimer
实例拥有一个Timer
实例来提供必要的调度。当使用start()
方法启动鸡蛋计时器时,它会安排 aTimerTask
在指定的分钟数后执行。当时间到时, 上的run()
方法TimerTask
由Timer
幕后调用,使其播放声音。然后应用程序在定时器被取消后终止。
安排重复性任务
Timer允许通过指定固定的执行速率或执行之间的固定延迟来安排任务重复执行。但是,有许多应用程序具有更复杂的调度要求。例如,每天早上在同一时间响起叫醒电话的闹钟不能简单地使用 86400000 毫秒(24 小时)的固定速率时间表,因为在时钟前进的日子里,闹钟会太晚或太早或向后(如果您的时区使用夏令时)。解决方案是使用日历算法来计算每日事件的下一次预定发生。这正是调度框架所支持的。考虑AlarmClock 清单 2 中的实现(请参阅相关链接以下载调度框架的源代码,以及包含框架和示例的 JAR 文件):
清单 2. AlarmClock 类
package org.tiling.scheduling.examples;
import java.text.SimpleDateFormat;
import java.util.Date;
import org.tiling.scheduling.Scheduler;
import org.tiling.scheduling.SchedulerTask;
import org.tiling.scheduling.examples.iterators.DailyIterator;
public class AlarmClock {
private final Scheduler scheduler = new Scheduler();
private final SimpleDateFormat dateFormat =
new SimpleDateFormat("dd MMM yyyy HH:mm:ss.SSS");
private final int hourOfDay, minute, second;
public AlarmClock(int hourOfDay, int minute, int second) {
this.hourOfDay = hourOfDay;
this.minute = minute;
this.second = second;
}
public void start() {
scheduler.schedule(new SchedulerTask() {
public void run() {
soundAlarm();
}
pri