java的定时器在平时应用到的还是很多的,比如它可以定时清理数据、定时清理缓存、定时弹出消息提示等等。我们现在做的一个项目应用的就比较多,我们主要应用它来监测程序的运行状态。用的地方这么多,那就有必要整理一下,翻了翻API,也上网查了查资料,发现了一个问题,网上有些人说Timer类的scheduleAtFixedRate()方法存在着线程同步的问题,可是API上介绍的Timer类是线程安全类啊。觉得谁的都不敢相信,还是自己验证一下比较好。
既然已经用到了Timer,那么就把基础的用法顺便也整理一下,首先写个最简单的定时器例子:
import java.io.IOException; import java.text.Format; import java.text.ParseException; import java.text.SimpleDateFormat; import java.util.Date; import java.util.Timer; import java.util.TimerTask; /** * Timer是一种线程设施,用于安排以后在后台线程中执行的任务。可安排任务执行一次,或者定期重复执行。 * Timer学习比较简单,按照API把Timer类中的方法弄懂就够用了。 * * 此类是线程安全的:多个线程可以共享单个 Timer 对象而无需进行外部同步。 * 这一点很重要(即使是使用scheduleAtFixedRate方法的时候碰见延迟问题,也不会出现线程同步的问题) * * schedule()方法有四种参数形式: * * (1)、void schedule(TimerTask task, Date time) 安排在指定时间执行指定任务(如果传递的参数时间已过,则立即执行任务) * * (2)、void schedule(TimerTask task, long delay) 安排在指定延迟后执行指定任务 * * (3)、void schedule(TimerTask task, Date firstTime, long period) 安排指定的任务在指定的时间开始进行重复的固定延迟执行。 * 在固定延迟执行中,根据前一次执行的实际执行时间来安排每次执行。如果由于任何原因(如垃圾回收或其他后台活动)而延迟了某次执行, * 则后续执行也将被延迟。从长期来看,执行的周期一般要略大于指定的周期。 * * (4)、schedule(TimerTask task, long delay, long period) 安排指定的任务在指定延迟后开始进行重复的固定延迟执行。 * <span style="white-space:pre"> </span>在固定延迟执行中,根据前一次执行的实际执行时间来安排每次执行。如果由于任何原因(如垃圾回收或其他后台活动)而延迟了某次执行, * 则后续执行也将被延迟。从长期来看,执行的周期一般要略大于指定的周期。 * * scheduleAtFixedRate()方法有两种参数形式 * 在固定速率执行中,相对于已安排的初始执行时间来安排每次执行。如果由于任何原因(如垃圾回收或其他背景活动)而延迟了某次执行, * 则将快速连续地出现两次或更多次执行,从而使后续执行能够赶上来。从长远来看,执行的频率将正好是指定周期的倒数 * * (1)、 void scheduleAtFixedRate(TimerTask task, Date firstTime, long period) 安排指定的任务在指定的时间开始进行重复的"固定速率"执行。 * * (2)、void scheduleAtFixedRate(TimerTask task, long delay, long period) 安排指定的任务在指定的延迟后开始进行重复的"固定速率"执行。 * * cancel() 方法:终止此计时器,丢弃所有当前已安排的任务。这不会干扰当前正在执行的任务(就是说已经进入run()方法的了就继续执行,如果还有其他排队的,那不执行)。 * <span style="white-space:pre"> </span> 一旦终止了计时器,那么它的执行线程也会终止,并且无法根据它安排更多的任务。 * * int purge() 从此计时器的任务队列中移除所有已取消的任务,如果没有对这些任务的外部引用,则它们就成为垃圾回收的合格对象。 以时间换取了空间。 一般程序不必要用。 * 返回值是从队列中移除的任务数。 * */ public class TimerTest { public static void main(String[] args) throws ParseException { final Timer timer = new Timer(); timer.schedule(new TimerTask(){ @Override public void run() { SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); Date date1=new Date(); String t1=df.format(date1);//取到格式化后的当前时间 String t2="2013-12-17 21:00:00"; Date date2 = null; try { date2 = df.parse(t2);//将字符串转换成时间格式 } catch (ParseException e) { // TODO Auto-generated catch block e.printStackTrace(); } System.out.println(date1.getTime()); //指定定时器结束的条件,要不然会该定时器会一直运行下去 if(date1.getTime()>=date2.getTime()){ System.out.println(t1+"定时器停止了"); timer.cancel();//终止该定时器 } } }, 1000,2000);// 在1秒后执行此任务,每次间隔2秒 } }
基础的用法解释都在代码的注释当中。我们着重要说的是schedure 与 schedureAtFixeRate这两个方法的区别以及schedureAtFixeRate方法是否存在同步问题 :
schedure (TimeTask task, Date t1, long period ) 方法在执行任务的时候如果某一任务执行过程中发生延迟,那么下面的任务时间都会顺延,也就是说,下一任务的启动是根据前一任务执行结束的时间 + 指定的间隔周期。
schedureAtFixeRate(TimeTask task, Date t1, long period ) 方法中一个任务的启动时间是根据前一任务的开始时间+指定的间隔周期。如果某一任务执行过程中发生延迟,那他后面的任务会迅速挨个执行,直到任务间隔周期正常。
注意后面的任务是迅速挨个执行的,而不是一起执行。
看个代码实例:
import java.util.Date; import java.util.Timer; import java.util.TimerTask; /** * 演示scheduleAtFixedRate与schedule的区别 * 证明scheduleAtFixedRate方法即使发生延迟的时候也是线程安全的。 * @author HaiCheng */ public class TimerTest2 { public static void main(String[] args) { final Timer t1= new Timer(); t1.schedule(new TimerTask() { int count=0; long flag=0;//时间标记 @Override public void run() { if(count!=0){ System.out.println("间隔时间---->"+(new Date().getTime()-flag)+"----count的值---->"+count); } flag=new Date().getTime();//上面那行先执行,下面这行后执行的 时间会略有偏差 ,差不多2毫秒左右 count++; if(count==3){ try { Thread.sleep(2000); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } } if(count==25){ t1.cancel(); } } }, 0, 100); } }
间隔时间---->100----count的值---->1 间隔时间---->100----count的值---->2 间隔时间---->2000----count的值---->3 间隔时间---->100----count的值---->4 间隔时间---->100----count的值---->5 间隔时间---->100----count的值---->6 间隔时间---->100----count的值---->7 间隔时间---->100----count的值---->8 间隔时间---->100----count的值---->9 间隔时间---->100----count的值---->10 间隔时间---->100----count的值---->11 间隔时间---->100----count的值---->12 间隔时间---->100----count的值---->13 间隔时间---->100----count的值---->14 间隔时间---->100----count的值---->15 间隔时间---->100----count的值---->16 间隔时间---->100----count的值---->17 间隔时间---->100----count的值---->18 间隔时间---->101----count的值---->19 间隔时间---->100----count的值---->20 间隔时间---->100----count的值---->21 间隔时间---->100----count的值---->22 间隔时间---->100----count的值---->23 间隔时间---->100----count的值---->24
间隔时间---->100----count的值---->1 间隔时间---->100----count的值---->2 间隔时间---->2000----count的值---->3 间隔时间---->0----count的值---->4 间隔时间---->0----count的值---->5 间隔时间---->0----count的值---->6 间隔时间---->0----count的值---->7 间隔时间---->0----count的值---->8 间隔时间---->0----count的值---->9 间隔时间---->0----count的值---->10 间隔时间---->0----count的值---->11 间隔时间---->0----count的值---->12 间隔时间---->0----count的值---->13 间隔时间---->0----count的值---->14 间隔时间---->0----count的值---->15 间隔时间---->0----count的值---->16 间隔时间---->0----count的值---->17 间隔时间---->0----count的值---->18 间隔时间---->0----count的值---->19 间隔时间---->0----count的值---->20 间隔时间---->0----count的值---->21 间隔时间---->0----count的值---->22 间隔时间---->98----count的值---->23 间隔时间---->100----count的值---->24
通过count的值打印出的顺序是对的,没有重复的,说明scheduleAtFixedRate 方法的任务发生延迟的时候也不会出现线程安全问题。