Java 中Timer 类的简洁用法
所有类型的 Java 应用程序一般都需要计划重复执行的任务。企业应用程序需要计划每日的日志或者晚间批处理过程。一个 J2SE或者 J2ME 日历应用程序需要根据用户的约定计划闹铃时间。不过,标准的调度类 Timer 和 TimerTask 没有足够的灵活性,无法支持通常需要的计划任务类型。在本文中,Java 开发人员 Tom White 向您展示了如何构建一个简单通用的计划框架,以用于执行任意复杂的计划任务。
我将把 java.util.Timer 和 java.util.TimerTask 统称为 Java 计时器框架,它们使程序员可以很容易地计划简单的任务(注意这些类也可用于 J2ME 中)。在 Java 2 SDK, Standard Edition, Version 1.3 中引入这个框架之前,开发人员必须编写自己的调度程序,这需要花费很大精力来处理线程和复杂的 Object.wait() 方法。不过,Java 计时器框架没有足够的能力来满足许多应用程序的计划要求。甚至一项需要在每天同一时间重复执行的任务,也不能直接使用 Timer 来计划,因为在夏令时开始和结束时会出现时间跳跃。
本文展示了一个通用的 Timer 和 TimerTask 计划框架,从而允许更灵活的计划任务。这个框架非常简单 ―― 它包括两个类和一个接口 ―― 并且容易掌握。如果您习惯于使用 Java 定时器框架,那么您应该可以很快地掌握这个计划框架(有关 Java 定时器框架的更多信息,请参阅 参考资料)。
计划单次任务
计划框架建立在 Java 定时器框架类的基础之上。因此,在解释如何使用计划框架以及如何实现它之前,我们将首先看看如何用这些类进行计划。
想像一个煮蛋计时器,在数分钟之后(这时蛋煮好了)它会发出声音提醒您。清单 1 中的代码构成了一个简单的煮蛋计时器的基本结构,它用 Java 语言编写:
清单 1. EggTimer 类
2
3import java.util.Timer;
4import java.util.TimerTask;
5
6public class EggTimer {
7 private final Timer timer = new Timer();
8 private final int minutes;
9
10 public EggTimer(int minutes) {
11 this.minutes = minutes;
12 }
13
14 public void start() {
15 timer.schedule(new TimerTask() {
16 public void run() {
17 playSound();
18 timer.cancel();
19 }
20 private void playSound() {
21 System.out.println("Your egg is ready!");
22 // Start a new thread to play a sound
23 }
24 }, minutes * 60 * 1000);
25 }
26
27 public static void main(String[] args) {
28 EggTimer eggTimer = new EggTimer(2);
29 eggTimer.start();
30 }
31
32}
33
EggTimer 实例拥有一个 Timer 实例,用于提供必要的计划。用 start() 方法启动煮蛋计时器后,它就计划了一个 TimerTask ,在指定的分钟数之后执行。时间到了, Timer 就在后台调用 TimerTask 的 start() 方法,这会使它发出声音。在取消计时器后这个应用程序就会中止。
计划重复执行的任务
通过指定一个固定的执行频率或者固定的执行时间间隔, Timer 可以对重复执行的任务进行计划。不过,有许多应用程序要求更复杂的计划。例如,每天清晨在同一时间发出叫醒铃声的闹钟不能简单地使用固定的计划频率 86400000 毫秒(24 小时),因为在钟拨快或者拨慢(如果您的时区使用夏令时)的那些天里,叫醒可能过晚或者过早。解决方案是使用日历算法计算每日事件下一次计划发生的时间。而这正是计划框架所支持的。考虑清单 2 中的 AlarmClock 实现(有关计划框架的源代码以及包含这个框架和例子的 JAR 文件,请参阅 参考资料):
清单 2. AlarmClock 类
2
3import java.text.SimpleDateFormat;
4
5import java.util.Date;
6
7import org.tiling.scheduling.Scheduler;
8import org.tiling.scheduling.SchedulerTask;
9import org.tiling.scheduling.examples.iterators.DailyIterator;
10
11public class AlarmClock {
12
13 private final Scheduler scheduler = new Scheduler();
14 private final SimpleDateFormat dateFormat =
15 new SimpleDateFormat("dd MMM yyyy HH:mm:ss.SSS");
16 private final int hourOfDay, minute, second;
17
18 public AlarmClock(int hourOfDay, int minute, int second) {
19 this.hourOfDay = hourOfDay;
20 this.minute = minute;
21 this.second = second;
22 }
23
24 public void start() {
25 scheduler.schedule(new SchedulerTask() {
26 public void run() {
27 soundAlarm();
28 }
29 private void soundAlarm() {
30 System.out.println("Wake up! " +
31 "It's " + dateFormat.format(new Date()));
32 // Start a new thread to sound an alarm
33 }
34 }, new DailyIterator(hourOfDay, minute, second));
35 }
36
37 public static void main(String[] args) {
38 AlarmClock alarmClock = new AlarmClock(7, 0, 0);
39 alarmClock.start();
40 }
41}
42
43
注意这段代码与煮蛋计时器应用程序非常相似。 AlarmClock 实例拥有一个 Scheduler (而不是 Timer )实例,用于提供必要的计划。启动后,这个闹钟对 SchedulerTask (而不是 TimerTask )进行调度用以发出报警声。这个闹钟不是计划一个任务在固定的延迟时间后执行,而是用 DailyIterator 类描述其计划。在这里,它只是计划任务在每天上午 7:00 执行。下面是一个正常运行情况下的输出:
Wake up! It's 24 Aug 2003 07:00:00.023
Wake up! It's 25 Aug 2003 07:00:00.001
Wake up! It's 26 Aug 2003 07:00:00.058
Wake up! It's 27 Aug 2003 07:00:00.015
Wake up! It's 28 Aug 2003 07:00:00.002
...
DailyIterator 实现了 ScheduleIterator ,这是一个将 SchedulerTask 的计划执行时间指定为一系列 java.util.Date 对象的接口。然后 next() 方法按时间先后顺序迭代 Date 对象。返回值 null 会使任务取消(即它再也不会运行)―― 这样的话,试图再次计划将会抛出一个异常。清单 3 包含 ScheduleIterator 接口:
清单 3. ScheduleIterator 接口
2
3import java.util.Date;
4
5public interface ScheduleIterator {
6 public Date next();
7}
DailyIterator 的 next() 方法返回表示每天同一时间(上午 7:00)的 Date 对象,如清单 4 所示。所以,如果对新构建的 next() 类调用 next() ,那么将会得到传递给构造函数的那个日期当天或者后面一天的 7:00 AM。再次调用 next() 会返回后一天的 7:00 AM,如此重复。为了实现这种行为, DailyIterator 使用了 java.util.Calendar 实例。构造函数会在日历中加上一天,对日历的这种设置使得第一次调用 next() 会返回正确的 Date 。注意代码没有明确地提到夏令时修正,因为 Calendar 实现(在本例中是 GregorianCalendar )负责对此进行处理,所以不需要这样做。
清单 4. DailyIterator 类
2
3import org.tiling.scheduling.ScheduleIterator;
4
5import java.util.Calendar;
6import java.util.Date;
7
8/** *//**
9* A DailyIterator class returns a sequence of dates on subsequent days
10* representing the same time each day.
11*/
12public class DailyIterator implements ScheduleIterator {
13 private final int hourOfDay, minute, second;
14 private final Calendar calendar = Calendar.getInstance();
15
16 public DailyIterator(int hourOfDay, int minute, int second) {
17 this(hourOfDay, minute, second, new Date());
18 }
19
20 public DailyIterator(int hourOfDay, int minute, int second, Date date) {
21 this.hourOfDay = hourOfDay;
22 this.minute = minute;
23 this.second = second;
24 calendar.setTime(date);
25 calendar.set(Calendar.HOUR_OF_DAY, hourOfDay);
26 calendar.set(Calendar.MINUTE, minute);
27 calendar.set(Calendar.SECOND, second);
28 calendar.set(Calendar.MILLISECOND, 0);
29 if (!calendar.getTime().before(date)) {
30 calendar.add(Calendar.DATE, -1);
31 }
32 }
33
34 public Date next() {
35 calendar.add(Calendar.DATE, 1);
36 return calendar.getTime();
37 }
38
39}
40</s
发表评论
-
JAVA Developer Sites
2009-09-24 22:10 618<!--blog template--> ... -
About serialVersionUID
2009-10-07 10:13 600The serializat ... -
Java HOTSPOT VM参数大全
2009-10-08 11:22 630SUN的JDK版本从1.3.1开始使用HotSpot虚拟机技术 ... -
JVM的垃圾回收机制详解和调优
2009-10-09 22:52 591原文地址: http:// ... -
eclipse中jboss启动时提示50 seconds 超时之真正原因
2009-11-02 23:20 715eclipse中jboss启 ... -
Linux下JDK路径的配置
2009-11-15 10:18 6981:安装linux下的jdk ... -
Eclipse中JBOSS5.1无法启动的问题解决办法
2010-03-19 14:15 746今天在Eclipse中启动JBoss 5.1时 ... -
How to write clone method
2010-04-21 10:51 748<!-- @page { margin: 2c ... -
linux下java GUI中文字体显示问题
2010-07-10 09:35 901很早以前就发现Java GU ... -
OpenJDK的完美中文显示
2010-07-11 23:51 680在 <meta http-equiv="CON ... -
Log4j的PatternLayout
2010-07-28 18:50 712The PatternLayout is by far the ... -
Ant 基础
2010-10-05 21:11 543Ant 生成文件剖析 Ant 没有定义它自己的自定义语法;相 ... -
Ant文件操作
2010-10-06 09:08 5008创建和删除目录 最 ... -
Java is Pass-by-Value!
2010-10-17 15:02 788<!--blog template--> ... -
Java的继承
2010-10-24 16:53 693<!--blog template--> ...
相关推荐
所有类型的 Java 应用程序一般都需要计划重复执行的任务。企业应用程序需要计划每日的日志或者晚间批处理过程。一个 J2SE 或者 J2ME 日历应用程序需要根据用户的约定计划闹铃时间。不过,标准的调度类 Timer 和 ...
所有类型的 Java 应用程序一般都需要计划重复执行的任务。企业应用程序需要计划每日的日志或者晚间批处理过程。一个 J2SE或者 J2ME 日历应用程序需要根据用户的约定计划闹铃时间。不过,标准的调度类 Timer 和 ...
在实际项目应用中经常会用到定时任务,可以通过quartz和spring的简单配置即可完成,但如果要改变任务的执行时间、频率,废弃任务等就需要改变配置甚至代码需要重启服务器,这里介绍一下如何通过quartz与spring的组合...
使用jfinal编写的本地应用,压缩文件里包括了eclipse本程序的源代码和可执行的jar文件。 通过配置数据库连接信息和要执行的sql语句,可实现定时执行多个sql语句。 所要执行的语句只能是写死的,可支持sqlserver ...
使用Java Swing写的一个小程序,用来定时关机、定时重启、定时执行某一个应用程序。执行时调用cmd命令执行的。其他的,就没什么了,最后用exe4j把jar打包成了exe文件,在本地测能用,不知道其他地方是否能用,内部有...
在 Java 应用程序中定时执行任务 IBM里下载的
在这个实战博客中,我们将创建一个Java任务调度器,演示如何使用Java编程语言和任务调度库来计划和执行定时任务。 以下是本实战博客的主要内容: 项目概述 准备工作 创建Java项目 添加任务调度库 设计任务 实现...
当你有很多事情需要提醒的时候,或者有很多重复的工作要做,定时通知器可以帮你很大的忙哦!目前版本的功能有: ...2. 定时运行应用程序 3. 定时天气预报 4. 定时关闭计算机 5. 定时模拟鼠标键盘 6. 定时更改桌面背景
在应用开发中,经常需要一些周期性的操作,比如每5分钟执行某一操作等。 对于这样的操作最方便、高效的实现方式就是使用java.util.Timer工具类。
java7在并发编程方面,带来了很多令人激动的新功能,这将使你的应用程序具备更好的并行任务性能。 《Java 7并发编程实战手册》是Java 7并发编程的实战指南,介绍了Java 7并发API中大部分重要而有用的机制。全书分为9...
某些应用程序中存在比较明显的任务边界,而在其他一些程序中则需要进一步分析才能揭示出粒度更细的并行性 任务的取消和关闭 任务取消 停止基于线程的服务 处理非正常的线程终止 JVM关闭 线程池...
编写一个Applet小应用程序能够显示时间,当用户最小化Applet时,程序会 被挂起,当还原Applet时,程序又会恢复执行,同时Applet中应显示挂起和恢复的时间。 (二)系统要求: 1、要求利用面向对象的方法以及Java的...
附加应用程序,可在启动时运行程序。 在开发(或打包)时,请注意,此应用程序需要使用与Termux主应用程序相同的密钥签名,才能获得执行脚本的权限。 安装 Termux:Boot应用程序可以从获得。 此外,我们为那些想...
Java版水果管理系统源码 任务调度系统介绍 简介 任务调度系统目的是对spring boot类型的java项目的cron类型...Executor是执行器,集成在每一个执行节点上,用来接收来自调度器的任务,并调用应用程序的对应方法执行,在
HelloWorldApp.java 第一个用Java开发的应用程序。 firstApplet.java 第一个用Java开发的Applet小程序。 firstApplet.htm 用来装载Applet的网页文件 第2章 示例描述:本章介绍开发Java的基础语法知识。 ...
Quartz完全使用Java开发,可以集成到各种规模的应用程序中。它能够承载成千上万的任务调度,并且支持集群。它支持将数据存储到数据库中以实现持久化,并支持绝大多数的数据库。它将任务与触发设计为松耦合,即一个...
跨平台性(Write Once, Run Anywhere): Java的代码可以在不同的平台上运行,只需编写一次代码,就可以在任何支持Java的设备上执行。这得益于Java虚拟机(JVM),它充当了代码和底层硬件之间的中介。 面向对象: ...
该项目以Linux内核为核心,围绕其构建了一个完整的操作系统,包括各种系统工具、库、应用程序和硬件支持。 以下是Linux项目的一些主要特点和资料介绍: 开放源代码:Linux项目的所有源代码都是公开的,并允许任何...
在主程序执行main函数就可以运行。你也可以打包你的应用为jar并通过使用java -jar来运行你的Web应用。它遵循"约定优先于配置"的原则, 使用SpringBoot只需很少的配置,大部分的时候直接使用默认的配置即可。同时...
6.1 在线程中执行任务93 6.1.1 串行地执行任务94 6.1.2 显式地为任务创建线程94 6.1.3 无限制创建线程的不足95 6.2 Executor框架96 6.2.1 示例:基于Executor的Web服务器97 6.2.2 执行策略98 6.2.3 线程池98...