java

位置:IT落伍者 >> java >> 浏览文章

Java语言中Timer类的简洁用法(二)


发布日期:2021年08月28日
 
Java语言中Timer类的简洁用法(二)

实现计划框架

在上一节我们学习了如何使用计划框架并将它与 Java 定时器框架进行了比较下面我将向您展示如何实现这个框架除了 清单 中展示的 ScheduleIterator 接口构成这个框架的还有另外两个类 —— Scheduler 和 SchedulerTask 这些类实际上在内部使用 Timer 和 SchedulerTask因为计划其实就是一系列的单次定时器

清单 显示了这两个类的源代码

清单 Scheduler

package orgtilingscheduling;

import javautilDate;

import javautilTimer;

import javautilTimerTask;

public class Scheduler {

class SchedulerTimerTask extends TimerTask {

private SchedulerTask schedulerTask;

private ScheduleIterator iterator;

public SchedulerTimerTask(SchedulerTask schedulerTask

ScheduleIterator iterator) {

thisschedulerTask = schedulerTask;

erator = iterator;

}

public void run() {

schedulerTaskrun();

reschedule(schedulerTask iterator);

}

}

private final Timer timer = new Timer();

public Scheduler() {

}

public void cancel() {

timercancel();

}

public void schedule(SchedulerTask schedulerTask

ScheduleIterator iterator) {

Date time = iteratornext();

if (time == null) {

schedulerTaskcancel();

} else {

synchronized(schedulerTasklock) {

if (schedulerTaskstate != SchedulerTaskVIRGIN) {

throw new IllegalStateException(Task already

scheduled + or cancelled);

}

schedulerTaskstate = SchedulerTaskSCHEDULED;

schedulerTasktimerTask =

new SchedulerTimerTask(schedulerTask iterator);

timerschedule(schedulerTasktimerTask time);

}

}

}

private void reschedule(SchedulerTask schedulerTask

ScheduleIterator iterator) {

Date time = iteratornext();

if (time == null) {

schedulerTaskcancel();

} else {

synchronized(schedulerTasklock) {

if (schedulerTaskstate != SchedulerTaskCANCELLED) {

schedulerTasktimerTask =

new SchedulerTimerTask(schedulerTask iterator);

timerschedule(schedulerTasktimerTask time);

}

}

}

}

}

清单 显示了 SchedulerTask 类的源代码

package orgtilingscheduling;

import javautilTimerTask;

public abstract class SchedulerTask implements Runnable {

final Object lock = new Object();

int state = VIRGIN;

static final int VIRGIN = ;

static final int SCHEDULED = ;

static final int CANCELLED = ;

TimerTask timerTask;

protected SchedulerTask() {

}

public abstract void run();

public boolean cancel() {

synchronized(lock) {

if (timerTask != null) {

timerTaskcancel();

}

boolean result = (state == SCHEDULED);

state = CANCELLED;

return result;

}

}

public long scheduledExecutionTime() {

synchronized(lock) {

return timerTask == null ? : timerTaskscheduledExecutionTime();

}

}

}

就像煮蛋计时器Scheduler 的每一个实例都拥有 Timer 的一个实例用于提供底层计划Scheduler 并没有像实现煮蛋计时器时那样使用一个单次定时器它将一组单次定时器串接在一起以便在由 ScheduleIterator 指定的各个时间执行 SchedulerTask 类

考虑 Scheduler 上的 public schedule() 方法 —— 这是计划的入口点因为它是客户调用的方法(在 取消任务 一节中将描述仅有的另一个 public 方法 cancel())通过调用 ScheduleIterator 接口的 next()发现第一次执行 SchedulerTask 的时间然后通过调用底层 Timer 类的单次 schedule() 方法启动计划在这一时刻执行为单次执行提供的 TimerTask 对象是嵌入的 SchedulerTimerTask 类的一个实例它包装了任务和迭代器(iterator)在指定的时间调用嵌入类的 run() 方法它使用包装的任务和迭代器引用以便重新计划任务的下一次执行reschedule() 方法与 schedule() 方法非常相似只不过它是 private 的并且执行一组稍有不同的 SchedulerTask 状态检查重新计划过程反复重复为每次计划执行构造一个新的嵌入类实例直到任务或者调度程序被取消(或者 JVM 关闭)

类似于 TimerTaskSchedulerTask 在其生命周期中要经历一系列的状态创建后它处于 VIRGIN 状态这表明它从没有计划过计划以后它就变为 SCHEDULED 状态再用下面描述的方法之一取消任务后它就变为 CANCELLED 状态管理正确的状态转变 —— 如保证不对一个非 VIRGIN 状态的任务进行两次计划 —— 增加了 Scheduler 和 SchedulerTask 类的复杂性在进行可能改变任务状态的操作时代码必须同步任务的锁对象

取消任务

取消计划任务有三种方式第一种是调用 SchedulerTask 的 cancel() 方法这很像调用 TimerTask 的 cancel()方法任务再也不会运行了不过已经运行的任务仍会运行完成 cancel() 方法的返回值是一个布尔值表示如果没有调用 cancel() 的话计划的任务是否还会运行更准确地说如果任务在调用 cancel() 之前是 SCHEDULED 状态那么它就返回 true如果试图再次计划一个取消的(甚至是已计划的)任务那么 Scheduler 就会抛出一个 IllegalStateException

取消计划任务的第二种方式是让 ScheduleIterator 返回 null这只是第一种方式的简化操作因为 Scheduler 类调用 SchedulerTask 类的 cancel()方法如果您想用迭代器而不是任务来控制计划停止时间时就用得上这种取消任务的方式了

第三种方式是通过调用其 cancel() 方法取消整个 Scheduler这会取消调试程序的所有任务并使它不能再计划任何任务

扩展 cron 实用程序

可以将计划框架比作 UNIX 的 cron 实用程序只不过计划次数的规定是强制性而不是声明性的例如在 AlarmClock 实现中使用的 DailyIterator 类它的计划与 cron 作业的计划相同都是由以 * * * 开始的 crontab 项指定的(这些字段分别指定分钟小时月和星期)

不过计划框架比 cron 更灵活想像一个在早晨打开热水的 HeatingController 应用程序我想指示它在每个工作日上午 : 打开热水在周未上午 : 打开热水使用 cron我需要两个 crontab 项( * * * * 而使用 ScheduleIterator 的解决方案更简洁一些因为我可以使用复合(composition)来定义单一迭代器清单 显示了其中的一种方法

清单 用复合定义单一迭代器

int[] weekdays = new int[] {

CalendarMONDAY

CalendarTUESDAY

CalendarWEDNESDAY

CalendarTHURSDAY

CalendarFRIDAY

};

int[] weekend = new int[] {

CalendarSATURDAY

CalendarSUNDAY

};

ScheduleIterator i = new CompositeIterator(

new ScheduleIterator[] {

new RestrictedDailyIterator( weekdays)

new RestrictedDailyIterator( weekend)

}

);

RestrictedDailyIterator 类很像 DailyIterator只不过它限制为只在一周的特定日子里运行而一个 CompositeIterator 类取得一组 ScheduleIterators并将日期正确排列到单个计划中

有许多计划是 cron 无法生成的但是 ScheduleIterator 实现却可以例如每个月的最后一天描述的计划可以用标准 Java 日历算法来实现(用 Calendar 类)               

上一篇:JavaMail常见问题之编程问题

下一篇:Java二进制兼容规则:域