所有类型的 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 定时器框架类的基础之上。因此,在解释如何使用计划框架以及如何实现它之前,我们将首先看看如何用这些类进行计划。
想像一个煮蛋计时器,在数分钟之后(这时蛋煮好了)它会发出声音提醒您。清单 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 实现:
清单 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 执行。下面是一个正常运行情况下的输出:
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 接口
package org.tiling.scheduling;
import java.util.Date;
public interface ScheduleIterator {
public Date next();
}
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 类
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();
}
}
转自:中国IT实验室
分享到:
相关推荐
MATLAB数字滤波器设计及其在语音信号去噪中的应用:源码详解与报告分享,MATLAB 数字滤波器设计 及其语音信号去噪应用。 (供学习交流)带源码,带注释。 有代码和报告。 ,核心关键词:MATLAB; 数字滤波器设计; 语音信号去噪应用; 源码; 注释; 代码与报告。,"MATLAB数字滤波器设计及其在语音信号去噪中的应用:带源码注释与报告"
COMSOL软件模拟三维电化学腐蚀过程的研究分析,comsol三维电化学腐蚀。 ,核心关键词:Comsol;三维电化学;腐蚀;模型模拟;电化学腐蚀过程。,"Comsol模拟:三维电化学腐蚀过程解析"
基于COMSOL的降雨入渗模型:边坡与渗流边界下的强度折减塑性形变研究,comsol降雨入渗模型,边坡降雨边界与渗流边界 强度折减塑性形变 ,comsol降雨入渗模型; 降雨边界; 渗流边界; 强度折减; 塑性形变,"COMSOL降雨入渗模型:边坡渗流与强度折减塑性形变分析"
2025员工安全意识培训试题及答案.docx
Python自动化办公源码-06在Word表格中将上下行相同内容的单元格自动合并
基于深度学习的神经网络技术在信息通信领域的应用研究.pdf
1.内容概要 通过KNN实现鸢尾花分类,即将新的数据点分配给已知类别中的某一类。该算法的核心思想是通过比较距离来确定最近邻的数据点,然后利用这些邻居的类别信息来决定待分类数据点的类别。 2.KNN算法的伪代码 对未知类别属性的数据集中的每个点依次执行以下操作: (1)计算已知类别数据集中的点与当前点之间的距离; (2)按照距离递增次序排序; (3)选取与当前点距离最小的k个点; (4)确定前k个点所在类别的出现频率; (5)返回前k个点出现频率最高的类别作为当前点的预测分类。 3.数据集说明 代码使用`pandas`库加载了一个名为`iris.arff.csv`的数据集 4.学习到的知识 通过鸢尾花分类学习了KNN算法,选择样本数据集中前k个最相似的数据,就是KNN算法中k的出处。k值过大,会出现分类结果模糊的情况;k值较小,那么预测的标签比较容易受到样本的影响。在实验过程中,不同的k值也会导致分类器的错误率不同。KNN算法精度高、无数据输入的假定,可以免去训练过程。但是对于数据量较多的训练样本,KNN必须保存全部数据集,可能会存在计算的时间复杂度、空间复杂度高的情况,存在维数灾难问
感应电机控制与矢量控制仿真:磁链闭环、转速闭环与电流闭环的综合应用研究,感应电机控制仿真,矢量控制,异步电机仿真,磁链闭环,转速闭环,电流闭环 ,核心关键词:感应电机控制仿真; 矢量控制; 异步电机仿真; 磁链闭环; 转速闭环; 电流闭环,"感应电机矢量控制仿真:磁链、转速、电流三闭环异步电机模拟"
威纶通TK6071IP触摸屏锁屏宏指令程序详解:注释清晰,便于理解与学习,威纶通触摸屏锁屏宏指令程序 ~ 威纶通触摸屏锁屏宏指令程序,TK6071IP触摸屏 利用宏指令程序来控制,宏指令注释清晰,方便理解程序。 具有很好的学习意义和借鉴价值。 ,关键词:威纶通触摸屏;锁屏宏指令程序;TK6071IP触摸屏;宏指令控制;注释清晰;学习借鉴。,威纶通触摸屏宏指令程序:清晰注释,学习借鉴之利器
2025输血相关法律法规试题考核试题及答案.docx
Python游戏编程源码-2048小游戏
2025最新康复医学概论考试题库(含答案).doc
Python自动化办公源码-09用Python批量往Word文档中指定位置添加图片
高品质车载充电器技术解决方案:含原理图、PCB图、C源代码及DSP控制器资料,附赠CDCDC模块资料,车载充电器 3Kw OBC 车载充电器 含原理图、PCB图、C源代码、变压器参数等生产资料。 附赠15kwdcdc模块资料 1、这款产品的方案采用的是dsp2803x系列。 2、原理图和Pcb采用AD绘制。 此方案仅供学习 ,车载充电器; 3Kw OBC; 原理图; PCB图; C源代码; 变压器参数; 生产资料; dsp2803x系列; AD绘制; 15kwdcdc模块资料,3Kw车载充电器方案:DSP2803x系列原理图、PCB图及C源学习包
2025最新康复医学考试题及答案.docx
内容概要:本文介绍了一种用于视频处理的新型卷积神经网络(CNN)加速器。主要创新点在于引入了混合精度计算、跨帧数据重用控制器及引擎,以及混合位宽差帧数据编码解码器。这些特性有效解决了视频帧间的时空相关性和稀疏性带来的挑战,提高了处理速度并降低了功耗和带宽需求。具体来说,通过对连续帧的数据相似度利用,可以在保持高精度的同时减少计算量和内存访问次数;通过多类型稀疏卷积聚类数组实现了对现代稀疏神经网络的支持;并通过混合位宽度编码减少了离芯片外的数据传输量,最高达到68%。 适用人群:从事深度学习硬件加速设计的研究人员和技术爱好者;关注AI边缘计算领域的从业者。 使用场景及目标:适用于自动驾驶汽车摄像头、监控系统等实时视频流应用场景。旨在为开发者提供高效的低能耗解决方案,在有限的时间和资源下完成大量的图像信号处理任务,如跟踪、分类等。 其他说明:文中还详细描述了芯片的设计细节,测试平台构建,以及不同模型(如MobileNet)在网络上的实际性能表现。
COMSOL电化学喷射腐蚀模拟与解析:技术原理及应用实践,comsol电化学喷射腐蚀 ,核心关键词:comsol; 电化学; 喷射腐蚀; 电化学腐蚀。,"电化学喷射腐蚀研究:comsol模拟与解析"
项目已获导师指导并通过的高分毕业设计项目,可作为课程设计和期末大作业,下载即用无需修改,项目完整确保可以运行。 包含:项目源码、数据库脚本、软件工具等,该项目可以作为毕设、课程设计使用,前后端代码都在里面。 该系统功能完善、界面美观、操作简单、功能齐全、管理便捷,具有很高的实际应用价值。 项目都经过严格调试,确保可以运行!可以放心下载 技术组成 语言:java 开发环境:idea 数据库:MySql8.0 部署环境:Tomcat(建议用 7.x 或者 8.x 版本),maven 数据库工具:navicat
直流无刷电机调速控制模型:速度环与电流环联合调控,PWM调制精确控制转速,该模型为直流无刷电机的调速控制,外环为速度环,速度输出为电流,内环为电流环,电流环输出为pwm占空比,占空比最终输入至逆变器进行PWM调制。 最后控制电机的转速 ,核心关键词:直流无刷电机; 调速控制; 外环速度环; 速度输出电流; 内环电流环; pwm占空比; 逆变器PWM调制; 控制电机转速。,直流无刷电机调速控制模型:内外环联动,PWM占空比驱动逆变器调速
基于MATLAB的含风光柴储微网多目标优化调度策略与模型实现,含风光柴储微网多目标优化调度 MATLAB代码 关键词:微网调度 风光柴储 粒子群算法 多目标优化 参考文档:《基于多目标粒子群算法的微电网优化调度》 仿真平台:MATLAB 平台采用粒子群实现求解 优势:代码注释详实,适合参考学习,非目前烂大街的版本,程序非常精品,请仔细辨识 主要内容:代码构建了含风机、光伏、柴油发电机以及储能电站在内的微网优化运行模型,并且考虑与上级电网的购电交易,综合考虑了多方经济成本以及风光新能源消纳等多方面的因素,从而实现微网系统的经济运行,求解采用的是MOPSO算法(多目标粒子群算法),求解效果极佳,具体可以看图 ,关键词:微网优化调度; 风光柴储; 粒子群算法; 多目标优化; MATLAB代码; MOPSO算法。,基于MATLAB的微网风光柴储多目标优化调度与MOPSO算法的实践研究