【测试详解】关于java定时器的常见问题,例如无法取消,被延期执行等

定时器Timer

  • Timer timer = new Timer(true);将定时器设置为守护线程(daemon),即当用户线程都已经执行完毕退出以后,jvm就会结束守护进程,不管守护进程是否还有任务,程序退出。

定时器任务TimerTask

  • 可以由计时器进行一次性或重复执行的任务,简单来说就是计时器要执行的任务。

ok,把两者联系起来使用就是:

timer.schedule(TimerTask task, long delay, long period)在指定 的延迟之后开始 ,重新执行 固定延迟执行的指定任务。

还有更多API也顺便在这里copy出来,看的时候方便:

    • voidcancel()

      终止此计时器,丢弃任何当前计划的任务。

      intpurge()

      从该计时器的任务队列中删除所有取消的任务。

      voidschedule(TimerTask task, Date time)

      在指定的时间安排指定的任务执行。

      voidschedule(TimerTask task, Date firstTime, long period)

      从指定 的时间开始 ,对指定的任务执行重复的 固定延迟执行

      voidschedule(TimerTask task, long delay)

      在指定的延迟之后安排指定的任务执行。

      voidschedule(TimerTask task, long delay, long period)

      在指定 的延迟之后开始 ,重新执行 固定延迟执行的指定任务。

      voidscheduleAtFixedRate(TimerTask task, Date firstTime, long period)

      从指定的时间 开始 ,对指定的任务执行重复的 固定速率执行

      voidscheduleAtFixedRate(TimerTask task, long delay, long period)

      在指定的延迟之后 开始 ,重新执行 固定速率的指定任务。

其实timer相当于一个后台单线程,timerTask就是timer里面的单个线程任务,timer除非设置成守护线程或则主动使用cancle方法终止计时器,否则会一直执行,而其中独立的timerTask也可以单独调用timerTask.cancle()方法终止任务,但是此方法只是终止了timer其中的某个独立的任务,timer仍然会继续执行:

例如:

Timer timer = new Timer();

TimerTask tt1 = new TimerTask(){public void run(){//执行代码}};

TimerTask tt2 = new TimerTask(){public void run(){//执行代码}};

timer.schedule(tt1,new Date(),5000);

timer.schedule(tt2,1000,5000);

调用其中的tt1的cancle方法只会终止tt1的任务,而timer和tt2任然会继续执行。

【附加说明】串行化特性:

       这样就是把tt1和tt2两个任务放入了timer线程当中串行执行,即tt1执行完才轮到tt2执行,t可以看到tt1和tt2其实应该是同时执行的,但是tt2延迟一秒钟期间,如果tt1还未执行完毕,那么tt2的开始执行时间因而会被推迟到tt1执行完毕后才开始执行,这就是为什么有时候发现为什么没有按时执行,可以从这里分析一下问题原因

 

----timer单线程

  • 对应于每个Timer对象是单个后台线程,用于依次执行所有定时器的所有任务。

public class TestTImerCircle2 {
	public static void main(String[] args) throws InterruptedException {
		Timer t = new Timer();
		//第一个任务
		TimerTask tt1 = new TimerTask() {
			@Override
			public void run() {
				// TODO Auto-generated method stub
				System.out.println("running1");
				try {
					Thread.sleep(2000);
				} catch (InterruptedException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
				System.out.println("running1 end");
			}
		};
		//第二个任务
		TimerTask tt2 = new TimerTask() {
			@Override
			public void run() {
				// TODO Auto-generated method stub
				System.out.println("running2");
				try {
					Thread.sleep(10000);
				} catch (InterruptedException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
				System.out.println("running2 end");
			}
		};
		t.schedule(tt2, new Date());
		t.schedule(tt1, new Date());
		
	}
}

 可以看到输出:

running2
running2 end
running1
running1 end

程序未终止

 running2 end执行在running1之前,如果是多线程并行running1会在running2 end之前,此处证明timer是单个线程后台。

需要区别与多线程并行的线程池执行任务:

​
public class ThreadPoolTest{
	public static void main(String[] args) throws InterruptedException {
		ExecutorService newCachedThreadPool = Executors.newCachedThreadPool();
		//第一个任务
		Runnable tt1 = new Runnable() {
			@Override
			public void run() {
				// TODO Auto-generated method stub
				System.out.println("running1");
				try {
					Thread.sleep(2000);
				} catch (InterruptedException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
				System.out.println("running1 end");
			}
		};
		//第二个任务
		Runnable tt2 = new Runnable() {
			@Override
			public void run() {
				// TODO Auto-generated method stub
				System.out.println("running2");
				try {
					Thread.sleep(2000);
				} catch (InterruptedException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
				System.out.println("running2 end");
			}
		};
		newCachedThreadPool.submit(tt1);
		newCachedThreadPool.submit(tt2);
		newCachedThreadPool.shutdown();
	}
}

​

 如上,tt1和tt2是同时进行的,输出结果是:

running2
running1
running1 end
running2 end

程序终止

----timer的终止

  • 空引用回收:在最后一次对Timer对象的引用后所有未完成的任务已完成执行,定时器的任务执行线程正常终止(并被收集到垃圾回收)。 但是,这可能需要任意长时间的发生。

  • 主动终止计时器:如果主叫方想要快速终止定时器的任务执行线程,则调用者应该调用定时器的cancel方法。


public class TestTImerCircle {
	public static void main(String[] args) {
		Timer t = new Timer();
		TimerTask tt = new TimerTask() {
			@Override
			public void run() {
				// TODO Auto-generated method stub
				System.out.println("running");
			}
		};
		t.schedule(tt, new Date());
        //方法一:置空等待回收
		t = null;
        //方法二:cancle终止
        //t.cancle();
		System.gc();
	}
}

执行结果: 

running

程序终止

以上,如果注释掉最后一行代码,发现很久(非常久,测试几个钟结束)都没有反应,因为jvm回收垃圾发生在任何时候,而主动调用gc回收发现程序立马终止,验证以上结论1。注释掉t=null,取消注释t.cancle()发现running并没有输出而是直接被终止,这是因为任务还没开始执行就被终止了,使用以下代码可以看到输出running:

public class TestTImerCircle2 {
	public static void main(String[] args) throws InterruptedException {
		Timer t = new Timer();
		TimerTask tt = new TimerTask() {
			@Override
			public void run() {
				// TODO Auto-generated method stub
				System.out.println("running");
			}
		};
		t.schedule(tt, new Date());
		Thread.sleep(100);
		t.cancel();
		
//		t = null;
//		System.gc();
		
	}
}

执行结果: 

running

程序终止

最重要的一种情况需要注意,如果任务正在执行,还未执行完,那么cancle()将不会引起正在进行的任务立马结束,而是等待当前任务完成以后,才终止计时器

public class TestTImerCircle2 {
	public static void main(String[] args) throws InterruptedException {
		Timer t = new Timer();
		TimerTask tt = new TimerTask() {
			@Override
			public void run() {
				// TODO Auto-generated method stub
				System.out.println("running");
				try {
					Thread.sleep(2000);
				} catch (InterruptedException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
				System.out.println("running end");
			}
		};
		t.schedule(tt, new Date());
		Thread.sleep(100);
		t.cancel();
		
//		t = null;
//		System.gc();
		
	}
}

执行结果: 

running

等待2秒钟

running end

程序终止

可以看到,两秒后才输出running end,另外cancle一旦触发,不管是否设置了重复执行,只要当前没有正在执行中的任务,立刻终止计时器: 

public class TestTImerCircle2 {
	public static void main(String[] args) throws InterruptedException {
		Timer t = new Timer();
		TimerTask tt = new TimerTask() {
			@Override
			public void run() {
				// TODO Auto-generated method stub
				System.out.println("running");
				try {
					Thread.sleep(2000);
				} catch (InterruptedException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
				System.out.println("running end");
			}
		};
		t.schedule(tt, new Date(),1);
		Thread.sleep(100);
		t.cancel();
		
//		t = null;
//		System.gc();
		
	}
}

执行结果:

running

执行2秒钟后

running end

程序终止

以上设置了隔1毫秒执行一次,但是执行完第一次后程序立刻被结束退出了,此处证明cancle具有延迟生效特性(有正在执行的任务)和必定执行特性(代码一旦被执行,必定在下一次任务前终止计时器)。

 

以上就介绍这么多,timer和timertask的使用应该足够了,然后还有一个是purge这个方法,从该计时器的任务队列中删除所有取消的任务,返回删除数,不知道为什么测试总是0,后续有空再更新测试吧。

 

 

对了,以上例子因为使用了timer.schedule(TimerTask timerTask,Date time)这个方法,在指定的时间安排指定的任务执行,并且只执行一次,所以如果碰到需要使用timer.schedule(TimerTask timerTask,Date time,long period)这种方法来重复间隔执行任务的情况,而达到条件要取消掉其中的一个timertask,那就可以在timertask的实现方法run中加入this.cancle()即可把timer中的该任务取消掉了,接着应该就可以使用timer.purge()这个方法清除掉,也可以不使用,等待jvm回收即可。

TimerTask tt2 = new TimerTask() {
			@Override
			public void run() {
				// 达到条件,结束
				this.cancle();
			}
		};

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值