转自http://huangtut.iteye.com/blog/405195
计时器可以提供运行基于时间的工作任务的功能,在计时器的管理下,特定的任务可在某一时间运行一次,也可以按指定的时间间隔反复运行。在众多厂商提供的计时器中应用得比较多的有以下三种:
一、 java.util.Timer
Sun JDK 提供的一种轻量级的计时器。
二、Commonj Timer
IBM 和 BEA 联合制定和推出的一种适用于 J2EE 环境的计时器。
三、WebSphere Application Server Scheduler
IBM WebSphere Application Server 提供的一种功能强大的计时器。
java.util.Timer
java.util.Timer 是 Sun JDK 提供的一种计时器,用于使后台线程按计划执行指定任务,这些任务可以被执行一次,也可以被定期执行。每个 Timer 对象对应一个后台线程,顺序地执行所有计时器任务。如果完成某个计时器任务的时间太长,那么它会“独占”计时器的任务执行线程,从而可能延迟后续任务的执行。对 Timer 对象最后的引用完成并且所有未处理的任务都已执行完成后,计时器的任务执行线程会正常终止(并且成为垃圾回收的对象)。以下为一个使用 java.util.Timer 的例子:
- view plaincopy to clipboardprint?
- import java.util.Timer;
- import java.util.TimerTask;
- publicclass TimerTest {
- Timer timer;
- public TimerTest(int seconds) {
- timer = new Timer();
- timer.schedule(new TimerTestTask(), seconds*1000);
- }
- class TimerTestTask extends TimerTask {
- publicvoid run() {
- System.out.println("In TimerTestTask, execute run method.");
- timer.cancel();
- }
- }
- publicstaticvoid main(String args[]) {
- System.out.println("Prepare to schedule task.");
- new TimerTest(2);
- System.out.println("Task scheduled.");
- }
- }
- import java.util.Timer;
- import java.util.TimerTask;
- publicclass TimerTest {
- Timer timer;
- public TimerTest(int seconds) {
- timer = new Timer();
- timer.schedule(new TimerTestTask(), seconds*1000);
- }
- class TimerTestTask extends TimerTask {
- publicvoid run() {
- System.out.println("In TimerTestTask, execute run method.");
- timer.cancel();
- }
- }
- publicstaticvoid main(String args[]) {
- System.out.println("Prepare to schedule task.");
- new TimerTest(2);
- System.out.println("Task scheduled.");
- }
- }
view plaincopy to clipboardprint? import java.util.Timer; import java.util.TimerTask; public class TimerTest { Timer timer; public TimerTest(int seconds) { timer = new Timer(); timer.schedule(new TimerTestTask(), seconds*1000); } class TimerTestTask extends TimerTask { public void run() { System.out.println("In TimerTestTask, execute run method."); timer.cancel(); } } public static void main(String args[]) { System.out.println("Prepare to schedule task."); new TimerTest(2); System.out.println("Task scheduled."); } } import java.util.Timer; import java.util.TimerTask; public class TimerTest { Timer timer; public TimerTest(int seconds) { timer = new Timer(); timer.schedule(new TimerTestTask(), seconds*1000); } class TimerTestTask extends TimerTask { public void run() { System.out.println("In TimerTestTask, execute run method."); timer.cancel(); } } public static void main(String args[]) { System.out.println("Prepare to schedule task."); new TimerTest(2); System.out.println("Task scheduled."); } }
java.util.Timer 简单易用,比较适合提供轻量级的计时器功能。由于其创建的线程会超出容器的管理范围,因此不能应用于管理的环境中。如果用户需要在 J2EE 环境中提供计时器功能,可考虑使用后面即将介绍的 Commonj Timer 或 WebSphere Application Server Scheduler。
Commonj Timer
Commonj Timer 是 Commonj 规范的一部分,它由 IBM 和 BEA 联合制定和推出,用以更好的响应客户和独立软件商的需求,给开发人员在开发可移植的服务端应用程序时提供一些更加简单和功能更加强大的方法。这个规范主要包括以下几个部分:Service Component Architecture,Service Data Objects,Work Manager and Timer 和 Enterprise Metadata Discovery。其中,Work Manager and Time 为在应用服务器中支持并发任务的执行提供了一些简单 API。这使用户可以方便地在 Servlet 和 EJB 中执行并发的计划任务,从而提高呑吐量,缩短服务端程序的响应时间,很好地解决了在 J2EE 环境中执行用户自定义的多线程并发与计时器服务的问题。
Commonj Timer API 包括三个接口:TimerManager, Timer 和 TimerListener。应用程序可以通过 TimerManager 来定期调用 TimerListener。每个 TimerManager 的 shcedule 方法返回一个 Timer 对象。用户可以通过 TimerManager 的 JNDI 名称在管理环境的上下文中查找 TimerManager。
用户可以通过以下三步来使用 Commonj Timer:
1. 在 web.xml 或者 ejb-jar.xml 中增加 Timer 的描述:
- view plaincopy to clipboardprint?
- <resource-ref>
- <res-ref-name>timer/MyTimer</res-ref-name>
- <res-type>commonj.timer.TimerManager</res-type>
- <res-auth>Container</res-auth>
- <res-sharing-scope>Unshareable</res-sharing-scope>
- </resource-ref>
- <resource-ref>
- <res-ref-name>timer/MyTimer</res-ref-name>
- <res-type>commonj.timer.TimerManager</res-type>
- <res-auth>Container</res-auth>
- <res-sharing-scope>Unshareable</res-sharing-scope>
- </resource-ref>
view plaincopy to clipboardprint? <resource-ref> <res-ref-name>timer/MyTimer</res-ref-name> <res-type>commonj.timer.TimerManager</res-type> <res-auth>Container</res-auth> <res-sharing-scope>Unshareable</res-sharing-scope> </resource-ref> <resource-ref> <res-ref-name>timer/MyTimer</res-ref-name> <res-type>commonj.timer.TimerManager</res-type> <res-auth>Container</res-auth> <res-sharing-scope>Unshareable</res-sharing-scope> </resource-ref>
2. 实现 TimerListener 接口:
- view plaincopy to clipboardprint?
- import commonj.timers.Timer;
- import commonj.timers.TimerListener;
- publicclass TestTimerListener implements TimerListener {
- private String input;
- public TestTimerListener(String input) {
- this.input = input;
- }
- publicvoid timerExpired(Timer timer) {
- Date timeValue = new Date();
- System.out.println("In timerExpired method, time is "
- + timeValue.toString() + ", input value is " + input);
- }
- }
- import commonj.timers.Timer;
- import commonj.timers.TimerListener;
- publicclass TestTimerListener implements TimerListener {
- private String input;
- public TestTimerListener(String input) {
- this.input = input;
- }
- publicvoid timerExpired(Timer timer) {
- Date timeValue = new Date();
- System.out.println("In timerExpired method, time is "
- + timeValue.toString() + ", input value is " + input);
- }
- }
view plaincopy to clipboardprint? import commonj.timers.Timer; import commonj.timers.TimerListener; public class TestTimerListener implements TimerListener { private String input; public TestTimerListener(String input) { this.input = input; } public void timerExpired(Timer timer) { Date timeValue = new Date(); System.out.println("In timerExpired method, time is " + timeValue.toString() + ", input value is " + input); } } import commonj.timers.Timer; import commonj.timers.TimerListener; public class TestTimerListener implements TimerListener { private String input; public TestTimerListener(String input) { this.input = input; } public void timerExpired(Timer timer) { Date timeValue = new Date(); System.out.println("In timerExpired method, time is " + timeValue.toString() + ", input value is " + input); } }
3. 查找 TimerManager,调用 TimerListener,初始化任务并设置时间:
- InitialContext ctx = new InitialContext();
- TimerManager mgr = (TimerManager)
- ctx.lookup("java:comp/env/timer/MyTimer");
- TimerListener listener =new TestTimerListener ("test");
- // 启动计时器
- mgr.schedule(listener, 1000*60);
- InitialContext ctx = new InitialContext();
- TimerManager mgr = (TimerManager)
- ctx.lookup("java:comp/env/timer/MyTimer");
- TimerListener listener =new TestTimerListener ("test");
- // 启动计时器
- mgr.schedule(listener, 1000*60);
InitialContext ctx = new InitialContext(); TimerManager mgr = (TimerManager) ctx.lookup("java:comp/env/timer/MyTimer"); TimerListener listener =new TestTimerListener ("test"); // 启动计时器 mgr.schedule(listener, 1000*60); InitialContext ctx = new InitialContext(); TimerManager mgr = (TimerManager) ctx.lookup("java:comp/env/timer/MyTimer"); TimerListener listener =new TestTimerListener ("test"); // 启动计时器 mgr.schedule(listener, 1000*60);
Commonj Timer 提供了一种在 J2EE 环境中使用计时器的方法,它解决了 java.util.Timer 创建的线程超出容器管理范围的问题。由于它不同于 JMX Timer Service 与 JMX framework 之间的紧耦合,从而提供了更加友好和独立的 API。 Commonj Timer API 中的 timer 是瞬时的、非事务性的,并且运行于创建它的 JVM 中,因此对于对持久性、事务性和可恢复性有要求的集群环境并不适合。
IBM WebSphere Application Server Scheduler
IBM WebSphere Application Server Scheduler 是一种功能全面的定时器服务,提供了在 WebSphere Application Server 中配置、管理和开发基于时间的工作任务的功能,能够使 J2EE 操作具有高性能、持久性以及事务性等特征。Scheduler 具有以下优点:
● 易于管理
Scheduler 的创建、更新、调度、验证以及监控等任务是 WebSphere Application Server 中的管理控制台进行配置的,可在单个服务器、集群、节点或单元中创建 Scheduler。每个配置后的 Scheduler 拥有唯一的 JNDI 名称、持久存储设备和守护程序。 图 1. WebSphere Application Server 管理控制台中的 Scheduler 配置面板
● 具有持久性和事务健壮性
Scheduler 任务可以通过存入关系数据库的方式被持久化,因此可以保证长期多次的运行。轮询守护程序使用这个数据库来确定哪些任务要运行以及什么时候运行。 ● 具有灵活的时间定制方式
Scheduler 任务依据用户指定的日历在某一时间开始执行一次或多次任务,用户可根据需要订制自己的日历。 ● 具有扩展性
当 Scheduler 服务运行于集群环境的时候,可以通过负载均衡管理提高性能和可用性。
图 2. 集群环境中的 Scheduler
使用 WebSphere Application Server Scheduler 服务可通过以下三个步骤:
● 在 WebSphere Application Server 管理控制台配置 Scheduler 服务; ● 开发调用 Scheduler 服务代码; ● 部署;
Scheduler 服务可由以下两种方式调用:调用会话 Bean 的任务,发送 Java 消息的服务。本文中使用调用会话 Bean 的任务说明如何调用 Scheduler 服务。
1. 创建一个 EJB 项目用以开发由会话 Bean 实现的任务,在 EJB Module 中创建一个无状态 Session Bean(ScheduledTask),在部署描述符中添加 com.ibm.websphere.scheduler.TaskHandlerHome 和 com.ibm.websphere.scheduler.TaskHandler 作为远程 Home 接口和远程接口。 2. 实现 ScheduledTask 的 process 方法。
- publicvoid process(TaskStatus status) {
- Date timeValue = new Date();
- System.out.println("In process method, time is " + timeValue.toString());
- }
- …
- …
- publicvoid process(TaskStatus status) {
- Date timeValue = new Date();
- System.out.println("In process method, time is " + timeValue.toString());
- }
- …
public void process(TaskStatus status) { Date timeValue = new Date(); System.out.println("In process method, time is " + timeValue.toString()); } … … public void process(TaskStatus status) { Date timeValue = new Date(); System.out.println("In process method, time is " + timeValue.toString()); } …
3. 在需要调用 Scheduler 服务的方法中查找 Scheduler,创建 BeanTaskInfo 实例并启动 scheduler。
- view plaincopy to clipboardprint?
- // 在J2EE上下文环境中查找scheduler
- Scheduler scheduler = (Scheduler)
- new InitialContext.lookup("java:comp/env/sched/MyTestScheduler");
- Object o = ctx.lookup("ejb/com/test/ScheduledTask");
- TaskHandlerHome home =
- (TaskHandlerHome)javax.rmi.PortableRemoteObject.narrow(o, TaskHandlerHome.class);
- // 设置任务启动时间
- java.util.Date startDate = new java.util.Date(System.currentTimeMillis()+3000);
- // 设置BeanTaskInfo
- BeanTaskInfo taskInfo = (BeanTaskInfo) scheduler.createTaskInfo(BeanTaskInfo.class);
- taskInfo.setName("testTask");
- taskInfo.setTaskHandler(home);
- taskInfo.setStartTime(startDate);
- taskInfo.setRepeatInterval("600seconds");
- taskInfo.setNumberOfRepeats(-1); //重复直至任务被取消
- taskInfo.setQOS(BeanTaskInfo.QOS_ATLEASTONCE);
- // 启动scheduler和任务
- TaskStatus status = scheduler.create(taskInfo);
- // 在J2EE上下文环境中查找scheduler
- Scheduler scheduler = (Scheduler)
- new InitialContext.lookup("java:comp/env/sched/MyTestScheduler");
- Object o = ctx.lookup("ejb/com/test/ScheduledTask");
- TaskHandlerHome home =
- (TaskHandlerHome)javax.rmi.PortableRemoteObject.narrow(o, TaskHandlerHome.class);
- // 设置任务启动时间
- java.util.Date startDate = new java.util.Date(System.currentTimeMillis()+3000);
- // 设置BeanTaskInfo
- BeanTaskInfo taskInfo = (BeanTaskInfo) scheduler.createTaskInfo(BeanTaskInfo.class);
- taskInfo.setName("testTask");
- taskInfo.setTaskHandler(home);
- taskInfo.setStartTime(startDate);
- taskInfo.setRepeatInterval("600seconds");
- taskInfo.setNumberOfRepeats(-1); //重复直至任务被取消
- taskInfo.setQOS(BeanTaskInfo.QOS_ATLEASTONCE);
- // 启动scheduler和任务
- TaskStatus status = scheduler.create(taskInfo);
view plaincopy to clipboardprint? // 在J2EE上下文环境中查找scheduler Scheduler scheduler = (Scheduler) new InitialContext.lookup("java:comp/env/sched/MyTestScheduler"); Object o = ctx.lookup("ejb/com/test/ScheduledTask"); TaskHandlerHome home = (TaskHandlerHome)javax.rmi.PortableRemoteObject.narrow(o, TaskHandlerHome.class); // 设置任务启动时间 java.util.Date startDate = new java.util.Date(System.currentTimeMillis()+3000); // 设置BeanTaskInfo BeanTaskInfo taskInfo = (BeanTaskInfo) scheduler.createTaskInfo(BeanTaskInfo.class); taskInfo.setName("testTask"); taskInfo.setTaskHandler(home); taskInfo.setStartTime(startDate); taskInfo.setRepeatInterval("600seconds"); taskInfo.setNumberOfRepeats(-1); //重复直至任务被取消 taskInfo.setQOS(BeanTaskInfo.QOS_ATLEASTONCE); // 启动scheduler和任务 TaskStatus status = scheduler.create(taskInfo); // 在J2EE上下文环境中查找scheduler Scheduler scheduler = (Scheduler) new InitialContext.lookup("java:comp/env/sched/MyTestScheduler"); Object o = ctx.lookup("ejb/com/test/ScheduledTask"); TaskHandlerHome home = (TaskHandlerHome)javax.rmi.PortableRemoteObject.narrow(o, TaskHandlerHome.class); // 设置任务启动时间 java.util.Date startDate = new java.util.Date(System.currentTimeMillis()+3000); // 设置BeanTaskInfo BeanTaskInfo taskInfo = (BeanTaskInfo) scheduler.createTaskInfo(BeanTaskInfo.class); taskInfo.setName("testTask"); taskInfo.setTaskHandler(home); taskInfo.setStartTime(startDate); taskInfo.setRepeatInterval("600seconds"); taskInfo.setNumberOfRepeats(-1); //重复直至任务被取消 taskInfo.setQOS(BeanTaskInfo.QOS_ATLEASTONCE); // 启动scheduler和任务 TaskStatus status = scheduler.create(taskInfo);
WebSphere Application Server Scheduler 易于管理,具有持久性和事务性,可扩展,可应用于集群环境,为 J2EE 应用提供了功能全面的计时器服务,但由于它与 WebSphere Application Server 的紧耦合关系,降低了它的灵活性,造成了一定的局限。
总结
java.util.Timer、 CommonJ Timer 和 WebSphere Application Server Scheduler 为用户提供了不同级别的、适用与不同范围的计时器,用户可以根据各自的需求使用不同的计时器,表 1 列出了 java.util.Timer、Commonj Timer 和 WebSphere Application Server Scheduler 之间比较结果,用户在使用计时器时可以用来参照比较。
表 1. 计时器比较结果 java.util.Timer 来源:Sun 优点:易于使用轻量级 缺点:创建的线程会超出容器管理范围 适用范围:非 J2EE 环境
Commonj Timer 来源:BEA and IBM 优点:解决了 java.util.Timer 创建的线程超出容器管理范围的问题;不同于 JMX Timer Service 与 JMX framework 之间的紧耦合,提供了更加友好和独立的 API 缺点:timer 是瞬时的、非事务性的,并且运行于创建它的 JVM 中,不适合于集群环境 适用范围:J2EE 普通环境
java.util.Timer 来源:WebSphere Application Server Scheduler 优点:易于管理 具有持久性和事务性 具有灵活的时间定制方式 具有扩展性,适用于集群环境 缺点:与 WebSphere Application Server 紧耦合 适用范围:J2EE 普通和集群环境