在 Java 应用程序中定时执行任务.docx
- 文档编号:11223261
- 上传时间:2023-05-29
- 格式:DOCX
- 页数:15
- 大小:38.79KB
在 Java 应用程序中定时执行任务.docx
《在 Java 应用程序中定时执行任务.docx》由会员分享,可在线阅读,更多相关《在 Java 应用程序中定时执行任务.docx(15页珍藏版)》请在冰点文库上搜索。
在Java应用程序中定时执行任务
在Java应用程序中定时执行任务
Java中Timer类的简洁用法
所有类型的Java应用程序一般都需要计划重复执行的任务。
企业应用程序需要计划每日的日志或者晚间批处理过程。
一个J2SE或者J2ME日历应用程序需要根据用户的约定计划闹铃时间。
不过,标准的调度类Timer和TimerTask没有足够的灵活性,无法支持通常需要的计划任务类型。
在本文中,Java开发人员TomWhite向您展示了如何构建一个简单通用的计划框架,以用于执行任意复杂的计划任务。
我将把java.util.Timer和java.util.TimerTask统称为Java计时器框架,它们使程序员可以很容易地计划简单的任务(注意这些类也可用于J2ME中)。
在Java2SDK,StandardEdition,Version1.3中引入这个框架之前,开发人员必须编写自己的调度程序,这需要花费很大精力来处理线程和复杂的Object.wait()方法。
不过,Java计时器框架没有足够的能力来满足许多应用程序的计划要求。
甚至一项需要在每天同一时间重复执行的任务,也不能直接使用Timer来计划,因为在夏令时开始和结束时会出现时间跳跃。
本文展示了一个通用的Timer和TimerTask计划框架,从而允许更灵活的计划任务。
这个框架非常简单――它包括两个类和一个接口――并且容易掌握。
如果您习惯于使用Java定时器框架,那么您应该可以很快地掌握这个计划框架(有关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()方法启动煮蛋计时器后,它就计划了一个TimerTask,在指定的分钟数之后执行。
时间到了,Timer就在后台调用TimerTask的start()方法,这会使它发出声音。
在取消计时器后这个应用程序就会中止。
计划重复执行的任务
通过指定一个固定的执行频率或者固定的执行时间间隔,Timer可以对重复执行的任务进行计划。
不过,有许多应用程序要求更复杂的计划。
例如,每天清晨在同一时间发出叫醒铃声的闹钟不能简单地使用固定的计划频率86400000毫秒(24小时),因为在钟拨快或者拨慢(如果您的时区使用夏令时)的那些天里,叫醒可能过晚或者过早。
解决方案是使用日历算法计算每日事件下一次计划发生的时间。
而这正是计划框架所支持的。
考虑清单2中的AlarmClock实现(有关计划框架的源代码以及包含这个框架和例子的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();
}
private void soundAlarm() {
System.out.println("Wake up!
" +
"It's " + dateFormat.format(new Date()));
// Start a new thread to sound an alarm...
}
}, new DailyIterator(hourOfDay, minute, second));
}
public static void main(String[] args) {
AlarmClock alarmClock = new AlarmClock(7, 0, 0);
alarmClock.start();
}
}
注意这段代码与煮蛋计时器应用程序非常相似。
AlarmClock实例拥有一个Scheduler(而不是Timer)实例,用于提供必要的计划。
启动后,这个闹钟对SchedulerTask(而不是TimerTask)进行调度用以发出报警声。
这个闹钟不是计划一个任务在固定的延迟时间后执行,而是用DailyIterator类描述其计划。
在这里,它只是计划任务在每天上午7:
00执行。
下面是一个正常运行情况下的输出:
Wakeup!
It's24Aug200307:
00:
00.023
Wakeup!
It's25Aug200307:
00:
00.001
Wakeup!
It's26Aug200307:
00:
00.058
Wakeup!
It's27Aug200307:
00:
00.015
Wakeup!
It's28Aug200307:
00:
00.002
...
DailyIterator实现了ScheduleIterator,这是一个将SchedulerTask的计划执行时间指定为一系列java.util.Date对象的接口。
然后next()方法按时间先后顺序迭代Date对象。
返回值null会使任务取消(即它再也不会运行)――这样的话,试图再次计划将会抛出一个异常。
清单3包含ScheduleIterator接口:
清单3.ScheduleIterator接口
package org.tiling.scheduling;
import java.util.Date;
public interface ScheduleIterator {
public Date next();
}
DailyIterator的next()方法返回表示每天同一时间(上午7:
00)的Date对象,如清单4所示。
所以,如果对新构建的next()类调用next(),那么将会得到传递给构造函数的那个日期当天或者后面一天的7:
00AM。
再次调用next()会返回后一天的7:
00AM,如此重复。
为了实现这种行为,DailyIterator使用了java.util.Calendar实例。
构造函数会在日历中加上一天,对日历的这种设置使得第一次调用next()会返回正确的Date。
注意代码没有明确地提到夏令时修正,因为Calendar实现(在本例中是GregorianCalendar)负责对此进行处理,所以不需要这样做。
清单4.DailyIterator类
package org.tiling.scheduling.examples.iterators;
import org.tiling.scheduling.ScheduleIterator;
import java.util.Calendar;
import java.util.Date;
/**
* A DailyIterator class returns a sequence of dates on subsequent days
* representing the same time each day.
*/
public class DailyIterator implements ScheduleIterator {
private final int hourOfDay, minute, second;
private final Calendar calendar = Calendar.getInstance();
public DailyIterator(int hourOfDay, int minute, int second) {
this(hourOfDay, minute, second, new Date());
}
public DailyIterator(int hourOfDay, int minute, int second, Date date) {
this.hourOfDay = hourOfDay;
this.minute = minute;
this.second = second;
calendar.setTime(date);
calendar.set(Calendar.HOUR_OF_DAY, hourOfDay);
calendar.set(Calendar.MINUTE, minute);
calendar.set(Calendar.SECOND, second);
calendar.set(Calendar.MILLISECOND, 0);
if (!
calendar.getTime().before(date)) {
calendar.add(Calendar.DATE, -1);
}
}
public Date next() {
calendar.add(Calendar.DATE, 1);
return calendar.getTime();
}
}
实现计划框架
在上一节,我们学习了如何使用计划框架,并将它与Java定时器框架进行了比较。
下面,我将向您展示如何实现这个框架。
除了清单3中展示的ScheduleIterator接口,构成这个框架的还有另外两个类――Scheduler和SchedulerTask。
这些类实际上在内部使用Timer和SchedulerTask,因为计划其实就是一系列的单次定时器。
清单5和6显示了这两个类的源代码:
清单5.Scheduler
package org.tiling.scheduling;
import java.util.Date;
import java.util.Timer;
import java.util.TimerTask;
public class Scheduler {
class SchedulerTimerTask extends TimerTask {
private SchedulerTask schedulerTask;
private ScheduleIterator iterator;
public SchedulerTimerTask(SchedulerTask schedulerTask,
ScheduleIterator iterator) {
this.schedulerTask = schedulerTask;
this.iterator = iterator;
}
public void run() {
schedulerTask.run();
reschedule(schedulerTask, iterator);
}
}
private final Timer timer = new Timer();
public Scheduler() {
}
public void cancel() {
timer.cancel();
}
public void schedule(SchedulerTask schedulerTask,
ScheduleIterator iterator) {
Date time = iterator.next();
if (time == null) {
schedulerTask.cancel();
} else {
synchronized(schedulerTask.lock) {
if (schedulerTask.state !
= SchedulerTask.VIRGIN) {
throw new IllegalStateException("Task already
scheduled " + "or cancelled");
}
schedulerTask.state = SchedulerTask.SCHEDULED;
schedulerTask.timerTask =
new SchedulerTimerTask(schedulerTask, iterator);
timer.schedule(schedulerTask.timerTask, time);
}
}
}
private void reschedule(SchedulerTask schedulerTask,
ScheduleIterator iterator) {
Date time = iterator.next();
if (time == null) {
schedulerTask.cancel();
} else {
synchronized(schedulerTask.lock) {
if (schedulerTask.state !
= SchedulerTask.CANCELLED) {
schedulerTask.timerTask =
new SchedulerTimerTask(schedulerTask, iterator);
timer.schedule(schedulerTask.timerTask, time);
}
}
}
}
}
清单6显示了SchedulerTask类的源代码:
清单6.SchedulerTask
package org.tiling.scheduling;
import java.util.TimerTask;
public abstract class SchedulerTask implements Runnable {
final Object lock = new Object();
int state = VIRGIN;
static final int VIRGIN = 0;
static final int SCHEDULED = 1;
static final int CANCELLED = 2;
TimerTask timerTask;
protected SchedulerTask() {
}
public abstract void run();
public boolean cancel() {
synchronized(lock) {
if (timerTask !
= null) {
timerTask.cancel();
}
boolean result = (state == SCHEDULED);
state = CANCELLED;
return result;
}
}
public long scheduledExecutionTime() {
synchronized(lock) {
return timerTask == null ?
0 :
timerTask.scheduledExecutionTime();
}
}
}
就像煮蛋计时器,Scheduler的每一个实例都拥有Timer的一个实例,用于提供底层计划。
Scheduler并没有像实现煮蛋计时器时那样使用一个单次定时器,它将一组单次定时器串接在一起,以便在由ScheduleIterator指定的各个时间执行SchedulerTask类。
考虑Scheduler上的publicschedule()方法――这是计划的入口点,因为它是客户调用的方法(在取消任务一节中将描述仅有的另一个public方法cancel())。
通过调用ScheduleIterator接口的next(),发现第一次执行SchedulerTask的时间。
然后通过调用底层Timer类的单次schedule()方法,启动计划在这一时刻执行。
为单次执行提供的TimerTask对象是嵌入的SchedulerTimerTask类的一个实例,它包装了任务和迭代器(iterator)。
在指定的时间,调用嵌入类的run()方法,它使用包装的任务和迭代器引用以便重新计划任务的下一次执行。
reschedule()方法与schedule()方法非常相似,只不过它是private的,并且执行一组稍有不同的SchedulerTask状态检查。
重新计划过程反复重复,为每次计划执行构造一个新的嵌入类实例,直到任务或者调度程序被取消(或者
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- Java 应用程序中定时执行任务 应用程序 定时 执行 任务