您的位置:首页 > 编程语言 > Java开发

JAVA的定时器总结

2013-12-17 22:33 295 查看
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


将上面例子的schedule方法换成scheduleAtFixedRate  执行结果变为:

间隔时间---->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  方法的任务发生延迟的时候也不会出现线程安全问题。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息