注册 登录  
 加关注
   显示下一条  |  关闭
温馨提示!由于新浪微博认证机制调整,您的新浪微博帐号绑定已过期,请重新绑定!立即重新绑定新浪微博》  |  关闭

东东的博客

江南烟雨,同大家一起分享

 
 
 

日志

 
 

如何正确使用Timer  

2009-10-09 08:58:25|  分类: java相关 |  标签: |举报 |字号 订阅

  下载LOFTER 我的照片书  |

在需要按时间计划执行简单任务的情况下,Timer是最常被使用到的工具类。使用Timer来调度TimerTask的实现者来执行任务,有两种方式,一种是使任务在指定时间被执行一次,另一种是从某一指定时间开始周期性地执行任务。

 

下面是一个简单的Timer例子,它每隔10秒钟执行一次特定操作doWork

Timer timer = new Timer();

                        TimerTask  task = new TimerTask (){

                        public void run() {

                             doWork();

                           }

};

              timer.schedule (task, 10000L, 10000L);

可以看到,具体的任务由TimerTask的子类实现,Timer负责管理、执行TimerTask

 

Timer 的使用

在不同的场景下,需要使用不同的Timer接口。如上所说,主要区分两种情况

1) 在指定时间执行任务,只执行一次

-          public void schedule(TimerTask task, long delay)

-          public void schedule(TimerTask task, Date time)

 

2)从指定时间开始,周期性地重复执行,直到任务被cancel掉。其中又分两种类型:

2.1 一种是按上一次任务执行的时间为依据,计算本次执行时间,可以称为相对时间法。比如,如果第一次任务是110秒执行的,周期为5秒,因系统繁忙(比如垃圾回收、虚拟内存切换),115秒没有得到机会执行,直到116秒才有机会执行第二次任务,那么第3次的执行时间将是121秒,偏移了1秒。

-          public void schedule(TimerTask task, long delay, long period)

-          public void schedule(TimerTask task, Date firstTime, long period)

 

2.2 另一种是绝对时间法,以用户设计的起始时间为基准,第n次执行时间为“起始时间+n*周期时间”。比如,在上面的情况下,虽然因为系统繁忙,第二执行时间被推后1秒,但第3次的时间点仍然应该是120秒。

-          public void scheduleAtFixedRate(TimerTask task, long delay, long period)

-          public void scheduleAtFixedRate(TimerTask task, Date firstTime,  long period)

 

相对时间法,关注于满足短时间内的执行间隔,绝对时间法,则更关注在一个长时间范围内,任务被执行的次数。如果我们要编写一个程序,用timer控制文档编辑器中提示光标的闪烁,用哪种更合适? 当然是相对时间法。如果改用绝对时间法,当从系统繁忙状态恢复后,光标会快速连续闪烁多次,以弥补回在系统繁忙期间没有被执行的任务,这样的情况会用户来说比较难以接受。又如,每10分钟检查一次新邮件的到来,也适合于使用相对时间法。

Timer timer = new Timer();

                        TimerTask  task = new TimerTask (){

                         public void run() {

                                displayCursor();

                           }

};

              timer.schedule (task, 1000L, 1000L); //每秒闪烁一次光标

 

作为对比,我们来考虑一种绝对时间法的应用场景——倒数任务,比如,要求在10秒内做倒数计时,每秒做一次doworkPerSecond操作,10秒结束时做一次doworkEnd操作,然后结束任务。

Timer timer = new Timer();

                        TimerTask  task = new TimerTask (){

                              private int count=10;

                              public void run() {

                                   if(count>0){

                                      doWorkPerSecond();

                                     count--;
                                          }else{

                               doWorkEnd();

                               cancel();

                             }

                           }

};

                        timer. scheduleAtFixedRate (task, 1000L, 1000L);

 

 

Timer及相关类的内部实现

-          Timer的内部会启动一个线程TimerThread。即使有多个任务被加入这个Timer,它始终只有一个线程来管理这些任务。

-          TimerThreadThread的子类。加入Timer的所有任务都会被最终放入TimerThread所管理的TaskQueue中。TimerThread会不断查看TaskQueue中的任务,取出当前时刻应该被执行的任务执行之,并且会重新计算该任务的下一次执行时间,重新放入TaskQueue。直到所有任务执行完毕(单次任务)或者被cancel(重复执行的任务),该线程才会结束。

-          TaskQueue,由数组实现的二叉堆,堆的排序是以任务的下一次执行时间为依据的。二叉堆的使用使得TimerThread以简洁高效的方式快速找到当前时刻需要执行的TimerTask,因为,堆排序的特性是保证最小(或者最大)值位于堆叠顶端,在这里,queue[1]始终是下次执行时间(nextExecutionTime)最小的,即应该最先被执行的任务

 

比如,同一个timer管理两个任务task1task2

timer.schedule (task1, 4000L, 10000L);

timer. scheduleAtFixedRate (task2, 2000L, 15000L);

则,TaskQueue中会有两个任务:task1task2task2会排在头部queue[1],当task2执行时间到,task2被执行,同时修改其nextExecutionTime =当前的nextExecutionTime +15000L(绝对时间法)并重新在二叉堆中排序。排序后,task1被放到头部。当task1执行时间到,task1被执行,并修改其nextExecutionTime =当前时间+10000L,然后重新在二叉堆中对其排序………

当收到客户端请求时,服务端生成一个Response对象。服务端希望客户端访问该对象的间隔时间不能超过20秒,否则,服务端认为客户端已经异常关闭或者网络异常,此时销毁掉该对象并打印错误日志。每次访问都会重新开始计时。

 

class Response{

private TimerTask  timeout;

            public void init(){

         ………

Timer timer = new Timer();

timeout = new TimeOutTask();

                        timer.schedule (timeout, 20000L);

}

 

public void invoke(){

    timeout.cancel();//取消当前的timeout任务

    ….

   timeout = new TimeOutTask();

                           timer.schedule (timeout, 20000L);//重新开始计时

}

 

void destroy(){

   ……..

}

 

class TimeOutTask extends TimerTask{

           public void run() {

TraceTool.error(“Time out, destroy the Response object.”);

destroy();

因为Timer不支持对任务重置计时,所以此处采取了先cancel当前的任务再重新加入新任务来达到重置计时的目的。注意,对一个已经cancel的任务,不能通过schedule重新加入Timer中执行。TimerTask的状态机如下:

 

一个新生成的TimerTask其状态为VIRGINTimer只接受状态为VIRGIN的任务,否则会有IllegalStateException异常抛出。

调用任务的cancel方法,该任务就转入CANCELLED状态,并很快从TaskQueue中删除。对单次执行的任务,一旦执行结束,该任务也会从中删除。这意味着TimerTask将不再被timer所执行了。
  评论这张
 
阅读(511)| 评论(0)
推荐 转载

历史上的今天

评论

<#--最新日志,群博日志--> <#--推荐日志--> <#--引用记录--> <#--博主推荐--> <#--随机阅读--> <#--首页推荐--> <#--历史上的今天--> <#--被推荐日志--> <#--上一篇,下一篇--> <#-- 热度 --> <#-- 网易新闻广告 --> <#--右边模块结构--> <#--评论模块结构--> <#--引用模块结构--> <#--博主发起的投票-->
 
 
 
 
 
 
 
 
 
 
 
 
 
 

页脚

网易公司版权所有 ©1997-2017