JAVA的定时器总结

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  方法的任务发生延迟的时候也不会出现线程安全问题。



你可能感兴趣的:(java,timer,schedure)