介绍
计时器可以提供运行基于时间的工作任务的功能,在计时器的管理下,特定的任务可在某一时间运行一次,也可以按指定的时间间隔反复运行。在众多厂商提供的计时器中应用得比较多的有以下三种:
● 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 的例子:
Java代码 <embed type="application/x-shockwave-flash" width="14" height="15" src="http://huangtut.iteye.com/javascripts/syntaxhighlighter/clipboard_new.swf" pluginspage="http://www.macromedia.com/go/getflashplayer" allowscriptaccess="always" quality="high" flashvars="clipboard=view%20plaincopy%20to%20clipboardprint%3F%0Aimport%20java.util.Timer%3B%20%20%20%0Aimport%20java.util.TimerTask%3B%20%20%20%0Apublic%20class%20TimerTest%20%7B%20%20%20%0A%20%20%20%20Timer%20timer%3B%20%20%20%0A%20%20%20%20public%20TimerTest(int%20seconds)%20%7B%20%20%20%0A%20%20%20%20%20%20%20%20timer%20%3D%20new%20Timer()%3B%20%20%20%0A%20%20%20%20%20%20%20%20timer.schedule(new%20TimerTestTask()%2C%20seconds*1000)%3B%20%20%20%0A%20%20%20%20%7D%20%20%20%0A%20%20%20%20class%20TimerTestTask%20extends%20TimerTask%20%7B%20%20%20%0A%20%20%20%20%20%20%20%20public%20void%20run()%20%7B%20%20%20%0A%20%20%20%20%20%20%20%20%20%20%20%20System.out.println(%22In%20TimerTestTask%2C%20execute%20run%20method.%22)%3B%20%20%20%0A%20%20%20%20%20%20%20%20%20%20%20%20timer.cancel()%3B%20%20%20%0A%20%20%20%20%20%20%20%20%7D%20%20%20%0A%20%20%20%20%7D%20%20%20%0A%20%20%20%20public%20static%20void%20main(String%20args%5B%5D)%20%7B%20%20%20%0A%20%20%20%20%20%20%20%20System.out.println(%22Prepare%20to%20schedule%20task.%22)%3B%20%20%20%0A%20%20%20%20%20%20%20%20new%20TimerTest(2)%3B%20%20%20%0A%20%20%20%20%20%20%20%20System.out.println(%22Task%20scheduled.%22)%3B%20%20%20%0A%20%20%20%20%7D%20%20%20%0A%7D%20%20%0Aimport%20java.util.Timer%3B%0Aimport%20java.util.TimerTask%3B%0Apublic%20class%20TimerTest%20%7B%0A%20%20%20%20Timer%20timer%3B%0A%20%20%20%20public%20TimerTest(int%20seconds)%20%7B%0A%20%20%20%20%20%20%20%20timer%20%3D%20new%20Timer()%3B%0A%20%20%20%20%20%20%20%20timer.schedule(new%20TimerTestTask()%2C%20seconds*1000)%3B%0A%20%20%20%20%7D%0A%20%20%20%20class%20TimerTestTask%20extends%20TimerTask%20%7B%0A%20%20%20%20%20%20%20%20public%20void%20run()%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20System.out.println(%22In%20TimerTestTask%2C%20execute%20run%20method.%22)%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20timer.cancel()%3B%20%0A%20%20%20%20%20%20%20%20%7D%0A%20%20%20%20%7D%0A%20%20%20%20public%20static%20void%20main(String%20args%5B%5D)%20%7B%0A%20%20%20%20%20%20%20%20System.out.println(%22Prepare%20to%20schedule%20task.%22)%3B%0A%20%20%20%20%20%20%20%20new%20TimerTest(2)%3B%0A%20%20%20%20%20%20%20%20System.out.println(%22Task%20scheduled.%22)%3B%0A%20%20%20%20%7D%0A%7D%0A%20%0A"></embed>
- 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 的描述:
Java代码 <embed type="application/x-shockwave-flash" width="14" height="15" src="http://huangtut.iteye.com/javascripts/syntaxhighlighter/clipboard_new.swf" pluginspage="http://www.macromedia.com/go/getflashplayer" allowscriptaccess="always" quality="high" flashvars="clipboard=view%20plaincopy%20to%20clipboardprint%3F%0A%3Cresource-ref%3E%20%20%20%0A%20%20%20%20%3Cres-ref-name%3Etimer%2FMyTimer%3C%2Fres-ref-name%3E%20%20%20%0A%20%20%20%20%3Cres-type%3Ecommonj.timer.TimerManager%3C%2Fres-type%3E%20%20%20%0A%20%20%20%20%3Cres-auth%3EContainer%3C%2Fres-auth%3E%20%20%20%0A%20%20%20%20%3Cres-sharing-scope%3EUnshareable%3C%2Fres-sharing-scope%3E%20%20%20%0A%3C%2Fresource-ref%3E%20%20%0A%20%20%20%20%20%20%3Cresource-ref%3E%0A%20%20%20%20%20%20%20%20%20%20%3Cres-ref-name%3Etimer%2FMyTimer%3C%2Fres-ref-name%3E%0A%20%20%20%20%20%20%20%20%20%20%3Cres-type%3Ecommonj.timer.TimerManager%3C%2Fres-type%3E%0A%20%20%20%20%20%20%20%20%20%20%3Cres-auth%3EContainer%3C%2Fres-auth%3E%0A%20%20%20%20%20%20%20%20%20%20%3Cres-sharing-scope%3EUnshareable%3C%2Fres-sharing-scope%3E%0A%20%20%20%20%20%20%3C%2Fresource-ref%3E%0A%20"></embed>
- 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 接口:
Java代码 <embed type="application/x-shockwave-flash" width="14" height="15" src="http://huangtut.iteye.com/javascripts/syntaxhighlighter/clipboard_new.swf" pluginspage="http://www.macromedia.com/go/getflashplayer" allowscriptaccess="always" quality="high" flashvars="clipboard=view%20plaincopy%20to%20clipboardprint%3F%0Aimport%20commonj.timers.Timer%3B%20%20%20%0Aimport%20commonj.timers.TimerListener%3B%20%20%20%0A%20%20%0Apublic%20class%20TestTimerListener%20implements%20TimerListener%20%7B%20%20%20%0A%20%20%20%20private%20String%20input%3B%20%20%20%0A%20%20%0A%20%20%20%20public%20TestTimerListener(String%20input)%20%7B%20%20%20%0A%20%20%20%20%20%20%20%20this.input%20%3D%20input%3B%20%20%20%0A%20%20%20%20%7D%20%20%20%0A%20%20%0A%20%20%20%20public%20void%20timerExpired(Timer%20timer)%20%7B%20%20%20%0A%20%20%20%20%20%20%20%20Date%20timeValue%20%3D%20new%20Date()%3B%20%20%20%0A%20%20%20%20%20%20%20%20System.out.println(%22In%20timerExpired%20method%2C%20time%20is%20%22%20%20%20%0A%20%20%20%20%20%20%20%20%20%20%20%20%2B%20timeValue.toString()%20%2B%20%22%2C%20input%20value%20is%20%22%20%2B%20input)%3B%20%20%20%0A%20%20%20%20%7D%20%20%20%0A%7D%20%20%0A%20%20%20%20%20%20import%20commonj.timers.Timer%3B%0A%20%20%20%20%20%20import%20commonj.timers.TimerListener%3B%0A%0A%20%20%20%20%20%20public%20class%20TestTimerListener%20implements%20TimerListener%20%7B%0A%20%20%20%20%20%20%20%20%20%20private%20String%20input%3B%0A%0A%20%20%20%20%20%20%20%20%20%20public%20TestTimerListener(String%20input)%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20this.input%20%3D%20input%3B%0A%20%20%20%20%20%20%20%20%20%20%7D%0A%0A%20%20%20%20%20%20%20%20%20%20public%20void%20timerExpired(Timer%20timer)%20%7B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20Date%20timeValue%20%3D%20new%20Date()%3B%0A%20%20%20%20%20%20%20%20%20%20%20%20%20%20System.out.println(%22In%20timerExpired%20method%2C%20time%20is%20%22%20%0A%20%20%20%20%20%20%09%09%20%20%20%20%2B%20timeValue.toString()%20%2B%20%22%2C%20input%20value%20is%20%22%20%2B%20input)%3B%0A%20%20%20%20%20%20%20%20%20%20%7D%0A%20%20%20%20%20%20%7D%0A%20%0A%0A"></embed>
- 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,初始化任务并设置时间:
Java代码 <embed type="application/x-shockwave-flash" width="14" height="15" src="http://huangtut.iteye.com/javascripts/syntaxhighlighter/clipboard_new.swf" pluginspage="http://www.macromedia.com/go/getflashplayer" allowscriptaccess="always" quality="high" flashvars="clipboard=InitialContext%20ctx%20%3D%20new%20InitialContext()%3B%20%20%20%0ATimerManager%20mgr%20%3D%20(TimerManager)%20%20%20%0A%20%20%20%20ctx.lookup(%22java%3Acomp%2Fenv%2Ftimer%2FMyTimer%22)%3B%20%20%20%0ATimerListener%20listener%20%3Dnew%20TestTimerListener%20(%22test%22)%3B%20%20%20%0A%2F%2F%20%E5%90%AF%E5%8A%A8%E8%AE%A1%E6%97%B6%E5%99%A8%20%20%20%0Amgr.schedule(listener%2C%201000*60)%3B%20%20%20%0A%20%20%20%20%20%20InitialContext%20ctx%20%3D%20new%20InitialContext()%3B%0A%20%20%20%20%20%20TimerManager%20mgr%20%3D%20(TimerManager)%0A%20%20%20%20%20%20%09ctx.lookup(%22java%3Acomp%2Fenv%2Ftimer%2FMyTimer%22)%3B%0A%20%20%20%20%20%20TimerListener%20listener%20%3Dnew%20TestTimerListener%20(%22test%22)%3B%0A%20%20%20%20%20%20%2F%2F%20%E5%90%AF%E5%8A%A8%E8%AE%A1%E6%97%B6%E5%99%A8%0A%20%20%20%20%20%20mgr.schedule(listener%2C%201000*60)%3B%20%0A%20%0A"></embed>
- 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. 集群环境