浅谈Java定时器发展

标签: java 定时器 发展 | 发表时间:2014-11-18 15:03 | 作者:
出处:http://www.iteye.com

java在jdk1.3中推出了定时器类Timer,而后在jdk1.5后由Dou Lea从新开发出了支持多线程的ScheduleThreadPoolExecutor,从后者的表现来看,可以考虑完全替代Timer了。

Timer与ScheduleThreadPoolExecutor对比:

1.

 

 Timer始于jdk1.3,其原理是利用一个TimerTask数组当作队列,将所有定时任务添加到此队列里面去。 然后启动一个线程,当队列为空时,此线程会阻塞,当队列里面有数据时,线程会去除一个TimerTask来判断

 是否到时间需要运行此任务,如果运行时间小于或等于当前时间时则开始运行任务。 由于其单线程的本质,所以会带来几个问题(详细代码在后面):

 第一,当我们添加到定时器中的任务比较耗时时,由于此定时器是单线程顺序执行定时器任务,所以会影响后续任务的按时执行。

 

//问题一示例:
m_timer.scheduleAtFixedRate(new TaskUseLongTime(), 1000, 5000);
m_timer.scheduleAtFixedRate(new TaskNormal(), 5000, 3000);

运行结果:
14:44:29: timer is sleeping 10 seconds
14:44:39: Task Normal executed
14:44:39: timer is sleeping 10 seconds
14:44:49: Task Normal executed
14:44:49: Task Normal executed
14:44:49: timer is sleeping 10 seconds

结果分析:TaskNormal任务无法保证3秒运行一次,其只能等待TaskUseLongTime运行结束后才可以。

 

 

 第二,Timer中的线程仅仅会捕获InterruptedException异常,所以如果我们自定义的定时任务里面没有捕获可能出现的异常而导致异常抛出后,

 

//问题二示例:
m_timer.schedule(new TaskThrowException(), 1000);
m_timer.schedule(new TaskNormal(), 2000);

运行结果:
14:47:37: Throw exception
Exception in thread "Timer-0" java.lang.RuntimeException
	at timer_test.TimerTest$TaskThrowException.run(TimerTest.java:85)
	at java.util.TimerThread.mainLoop(Timer.java:512)
	at java.util.TimerThread.run(Timer.java:462)

结果分析:
当前一个任务抛出异常后,后面的TaskNormal任务无法继续运行

 

 

 会导致我们的Timer线程停止,从而另后续的任务无法执行。

 第三,其无法处理多个同时发生的定时任务

 

//问题三示例:
m_timer.scheduleAtFixedRate(new TaskUseLongTime("timer1"), 1000, 15000);
m_timer.scheduleAtFixedRate(new TaskUseLongTime("timer2"), 1000, 15000);

运行结果:
14:50:16: timer1 is sleeping 10 seconds
14:50:26: timer2 is sleeping 10 seconds
14:50:36: timer2 is sleeping 10 seconds

结果分析:
我的启动时间均是1秒以后,但是timer1和timer2启动的时间明显不一致

 

 

 

代码示例:

 

/**
 * @filename TimerTest.java
 * @date     2014-11-18 
 */
package timer_test;

import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Timer;
import java.util.TimerTask;

public class TimerTest
{
	private final Timer m_timer = new Timer();
	
	public static void main(String[] args)
	{
		new TimerTest().test();
	}
	
	public void test()
	{
		//问题一示例:
		m_timer.scheduleAtFixedRate(new TaskUseLongTime(), 1000, 5000);
		m_timer.scheduleAtFixedRate(new TaskNormal(), 5000, 3000);
		
		//问题二示例:
//		m_timer.schedule(new TaskThrowException(), 1000);
//		m_timer.schedule(new TaskNormal(), 2000);
		
		//问题三示例:
//		m_timer.scheduleAtFixedRate(new TaskUseLongTime("timer1"), 1000, 5000);
//		m_timer.scheduleAtFixedRate(new TaskUseLongTime("timer2"), 1000, 5000);

	}
	
	private class TaskUseLongTime extends TimerTask
	{
		private String m_taskName = "timer";
		public TaskUseLongTime(){}
		
		public TaskUseLongTime(String taskName)
		{
			m_taskName = taskName;
		}
		@Override
		public void run()
		{
			try
			{
				System.out.println(getCurrentTime()+": "+m_taskName+" is sleeping 10 seconds");
				Thread.sleep(10000);
			} catch (InterruptedException e)
			{
			}
		}
	}
	
	private class TaskNormal extends TimerTask
	{
		@Override
		public void run()
		{
			System.out.println(getCurrentTime()+": Task Normal executed");
		}
	}
	
	private class TaskThrowException extends TimerTask
	{
		@Override
		public void run()
		{
			System.out.println(getCurrentTime()+": Throw exception");
			throw new RuntimeException();
		}
	}
	
	private String getCurrentTime()
	{
		return new SimpleDateFormat("HH:mm:ss").format(new Date());
	}
}

 

 

2.ScheduleThreadPoolExecutor

 

ScheduleThreadPoolExecutor始于jdk1.5,是由Dou Lea先生编写的,其利用ThreadPoolExecutor和DelayQueue巧妙的结合完成了多线程定时器的实现,解决了Timer中由于单线程而导致的上述三个缺陷。

 

问题一中的问题是因为单线程顺序执行导致后续任务无法按时完成,我们看到多线程可以很容易的解决此问题,同时我们注意到TaskUseLongTime的执行时间为10s(请看后续代码),我们定时任务间隔是5秒,但是从结果中发现我们的任务执行间隔却是10秒,所以我们可以判断ScheduleThreadPoolExecutor是采用每线程每任务的模式工作的。

 

//问题一:
m_timer.scheduleAtFixedRate(new TaskUseLongTime(), 1000, 5000, TimeUnit.MILLISECONDS);
m_timer.scheduleAtFixedRate(new TaskNormal(), 1000, 5000, TimeUnit.MILLISECONDS);

运行结果:
14:54:37: Task Normal executed
14:54:37: timer is sleeping 10 seconds
14:54:42: Task Normal executed
14:54:47: Task Normal executed
14:54:47: timer is sleeping 10 seconds
14:54:52: Task Normal executed

 

 

问题二中我们发现当抛出异常的任务执行后不影响其他任务的运行,同时我们发现在运行结果里面没有将我们的异常抛出,这是因为ScheduleThreadPoolExecutor类在执行完定时任务后会返回一个ScheduledFuture运行结果,不论结果是顺利完成还是有异常均会保存在这里。

 

 

//问题二:
m_timer.scheduleAtFixedRate(new TaskThrowException(), 1000, 5000, TimeUnit.MILLISECONDS);
m_timer.scheduleAtFixedRate(new TaskNormal(), 1000, 5000, TimeUnit.MILLISECONDS);

运行结果:
14:58:36: Throw exception
14:58:36: Task Normal executed
14:58:41: Task Normal executed
14:58:46: Task Normal executed
14:58:51: Task Normal executed
14:58:56: Task Normal executed

 

 问题三由于是多线程所以我们可以保证我们的定时任务可以同时执行

//问题三:
m_timer.scheduleAtFixedRate(new TaskUseLongTime("timer1"), 1000, 5000, TimeUnit.MILLISECONDS);
m_timer.scheduleAtFixedRate(new TaskUseLongTime("timer2"), 1000, 5000, TimeUnit.MILLISECONDS);

运行结果:
15:01:12: timer1 is sleeping 10 seconds
15:01:12: timer2 is sleeping 10 seconds
15:01:22: timer2 is sleeping 10 seconds
15:01:22: timer1 is sleeping 10 seconds
15:01:32: timer1 is sleeping 10 seconds
15:01:32: timer2 is sleeping 10 seconds

 

 详细代码:

/**
 * @filename ScheduleThreadPoolExecutorTest.java
 * @date     2014-11-18 
 */
package timer_test;

import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.concurrent.Callable;
import java.util.concurrent.ScheduledThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

public class ScheduleThreadPoolExecutorTest
{
	private final ScheduledThreadPoolExecutor m_timer = new ScheduledThreadPoolExecutor(10);
	
	public static void main(String[] args)
	{
		ScheduleThreadPoolExecutorTest timerTest = new ScheduleThreadPoolExecutorTest();
		timerTest.test();
		
		try
		{
			Thread.sleep(100000);
		} catch (InterruptedException e)
		{
		}finally
		{
			timerTest.shutdown();
		}
	}
	
	public void shutdown()
	{
		m_timer.shutdown();
	}
	
	public void test()
	{
		//问题一:
//		m_timer.scheduleAtFixedRate(new TaskUseLongTime(), 1000, 5000, TimeUnit.MILLISECONDS);
//		m_timer.scheduleAtFixedRate(new TaskNormal(), 1000, 5000, TimeUnit.MILLISECONDS);
		
		//问题二:
//		m_timer.scheduleAtFixedRate(new TaskThrowException(), 1000, 5000, TimeUnit.MILLISECONDS);
//		m_timer.scheduleAtFixedRate(new TaskNormal(), 1000, 5000, TimeUnit.MILLISECONDS);
		
		//问题三:
		m_timer.scheduleAtFixedRate(new TaskUseLongTime("timer1"), 1000, 5000, TimeUnit.MILLISECONDS);
		m_timer.scheduleAtFixedRate(new TaskUseLongTime("timer2"), 1000, 5000, TimeUnit.MILLISECONDS);
		
	}
	
	private class TaskUseLongTime implements Callable<Integer>, Runnable
	{
		private String m_taskName = "timer";
		private TaskUseLongTime(){}
		
		private TaskUseLongTime(String taskName)
		{
			m_taskName = taskName;
		}
		
		public void run()
		{
			try
			{
				System.out.println(getCurrentTime()+": "+m_taskName+" is sleeping 10 seconds");
				Thread.sleep(10000);
			} catch (InterruptedException e)
			{
			}
		}

		public Integer call() throws Exception
		{
			run();
			return 0;
		}
	}
	
	@SuppressWarnings("unused")
	private class TaskNormal implements Callable<Integer>, Runnable
	{
		
		public Integer call() throws Exception
		{
			run();
			return 0;
		}

		public void run()
		{
			System.out.println(getCurrentTime()+": Task Normal executed");
		}
	}
	
	@SuppressWarnings("unused")
	private class TaskThrowException implements Callable<Integer>, Runnable
	{
		public Integer call() throws Exception
		{
			System.out.println(getCurrentTime()+": Throw exception");
			throw new RuntimeException();
		}

		public void run()
		{
			System.out.println(getCurrentTime()+": Throw exception");
			throw new RuntimeException();
		}
	}
	
	
	private String getCurrentTime()
	{
		return new SimpleDateFormat("HH:mm:ss").format(new Date());
	}
}

 

 

 

 

 



已有 0 人发表留言,猛击->> 这里<<-参与讨论


ITeye推荐



相关 [java 定时器 发展] 推荐:

浅谈Java定时器发展

- - ITeye博客
java在jdk1.3中推出了定时器类Timer,而后在jdk1.5后由Dou Lea从新开发出了支持多线程的ScheduleThreadPoolExecutor,从后者的表现来看,可以考虑完全替代Timer了. Timer与ScheduleThreadPoolExecutor对比:.  Timer始于jdk1.3,其原理是利用一个TimerTask数组当作队列,将所有定时任务添加到此队列里面去.

Java中定时器

- - BlogJava-首页技术区
对于开发游戏项目的同胞来说,Timer 这个东西肯定不会陌生,今天放假闲来无事,对以前自己经常使用的定时进行了一番小小的总结. 没有写具体实现的原理,只是列举出了其中的四种比较常见的使用方法,相对而言,所以只要按照其所列举的例子仿照即可.     // 第一种方法:设定指定任务task在指定时间time执行 schedule(TimerTask task, Date time).

Spring定时器StopWatch

- - 编程语言 - ITeye博客
简单总结一句,Spring提供的计时器StopWatch对于秒、毫秒为单位方便计时的程序,尤其是单线程、顺序执行程序的时间特性的统计输出支持比较好. 也就是说假如我们手里面有几个在顺序上前后执行的几个任务,而且我们比较关心几个任务分别执行的时间占用状况,希望能够形成一个不太复杂的日志输出,StopWatch提供了这样的功能.

Java中的锁(Locks in Java)

- - 并发编程网 - ifeve.com
原文链接 作者:Jakob Jenkov 译者:申章 校对:丁一. 锁像synchronized同步块一样,是一种线程同步机制,但比Java中的synchronized同步块更复杂. 因为锁(以及其它更高级的线程同步机制)是由synchronized同步块的方式实现的,所以我们还不能完全摆脱synchronized关键字( 译者注:这说的是Java 5之前的情况).

Java PaaS 对决

- 呆瓜 - IBM developerWorks 中国 : 文档库
本文为 Java 开发人员比较了三种主要的 Platform as a Service (PaaS) 产品:Google App Engine for Java、Amazon Elastic Beanstalk 和 CloudBees RUN@Cloud. 它分析了每种服务独特的技术方法、优点以及缺点,而且还讨论了常见的解决方法.

Java浮点数

- d0ngd0ng - 译言-电脑/网络/数码科技
Thomas Wang, 2000年3月. Java浮点数的定义大体上遵守了二进制浮点运算标准(即IEEE 754标准). IEEE 754标准提供了浮点数无穷,负无穷,负零和非数字(Not a number,简称NaN)的定义. 在Java开发方面,这些东西经常被多数程序员混淆. 在本文中,我们将讨论计算这些特殊的浮点数相关的结果.

Qt——转战Java?

- - 博客 - 伯乐在线
编者按:事实上,在跨平台开发方面,Qt仍是最好的工具之一,无可厚非,但Qt目前没有得到任何主流移动操作系统的正式支持. 诺基亚的未来计划,定位非常模糊,这也是令很多第三方开发者感到失望,因此将导致诺基亚屡遭失败的原因. Qt的主要开发者之一Mirko Boehm在博客上强烈讽刺Nokia裁了Qt部门的决定,称其为“绝望之举”,而非“策略变更”.

java 验证码

- - ITeye博客
// 创建字体,字体的大小应该根据图片的高度来定. // 随机产生160条干扰线,使图象中的认证码不易被其它程序探测到. // randomCode用于保存随机产生的验证码,以便用户登录后进行验证. // 随机产生codeCount数字的验证码. // 得到随机产生的验证码数字. // 产生随机的颜色分量来构造颜色值,这样输出的每位数字的颜色值都将不同.

Java异常

- - CSDN博客推荐文章
“好的程序设计语言能够帮助程序员写出好程序,但是无论哪种语言都避免不了程序员写出坏的程序.                                                                                                                          ----《Java编程思想》.

java面试题

- - Java - 编程语言 - ITeye博客
 抽象就是忽略一个主题中与当前目标无关的那些方面,以便更充分地注意与当前目标有关的方面. 抽象并不打算了解全部问题,而只是选择其中的一部分,暂时不用部分细节. 抽象包括两个方面,一是过程抽象,二是数据抽象.  继承是一种联结类的层次模型,并且允许和鼓励类的重用,它提供了一种明确表述共性的方法. 对象的一个新类可以从现有的类中派生,这个过程称为类继承.