浅谈java定时器的发展历程

 更新时间:2017年12月13日 15:01:59   作者:hacksin  
这篇文章主要介绍了浅谈java定时器的发展历程,具有一定借鉴价值,需要的朋友可以参考下。

在开发中,我们经常需要一些周期性的操作,例如每隔几分钟就进行某一项操作。这时候我们就要去设置个定时器,Java中最方便、最高效的实现方式是用java.util.Timer工具类,再通过调度java.util.TimerTask任务。

Timer是一种工具,线程用其安排以后在后台线程中执行的任务。可安排任务执行一次,或者定期重复执行。实际上是个线程,定时调度所拥有的TimerTasks。

TimerTask是一个抽象类,它的子类由Timer安排为一次执行或重复执行的任务。实际上就是一个拥有run方法的类,需要定时执行的代码放到run方法体内。

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

Timer与ScheduleThreadPoolExecutor对比:

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

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

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

Java代码

//问题一示例: 
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启动的时间明显不一致 

代码示例:

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,是由DouLea先生编写的,其利用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 

详细代码:

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());
	}
}

总结

以上就是本文关于浅谈java定时器的发展历程的全部内容,希望对大家有所帮助。感兴趣的朋友可以继续参阅本站:

Java实现一个简单的定时器代码解析

Java多线程定时器Timer原理及实现

java定时器timer的使用方法代码示例

如有不足之处,欢迎留言指出。感谢朋友们对本站的支持!

相关文章

  • Java读写Windows共享文件夹的方法实例

    Java读写Windows共享文件夹的方法实例

    本篇文章主要介绍了Java读写Windows共享文件夹的方法实例,具有一定的参考价值,有兴趣的同学可以了解一下。
    2016-11-11
  • Java volatile 关键字介绍与使用示例详解

    Java volatile 关键字介绍与使用示例详解

    这篇文章详细介绍了Java中的volatile关键字,包括它的核心特性、如何保证变量的可见性和有序性,以及它在解决多线程问题中的局限性,文章通过示例展示了如何在实际编程中使用volatile,并解释了如何通过其他同步机制来弥补volatile的不足,感兴趣的朋友一起看看吧
    2025-01-01
  • springboot项目组引入JMeter的实现步骤

    springboot项目组引入JMeter的实现步骤

    本文主要介绍了springboot项目组引入JMeter的实现步骤,文中通过示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2021-09-09
  • 详解java CountDownLatch和CyclicBarrier在内部实现和场景上的区别

    详解java CountDownLatch和CyclicBarrier在内部实现和场景上的区别

    这篇文章主要介绍了详解java CountDownLatch和CyclicBarrier在内部实现和场景上的区别,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2020-05-05
  • Flowable ReceiveTask使用场景分析

    Flowable ReceiveTask使用场景分析

    这篇文章主要为大家介绍了Flowable ReceiveTask使用场景分析,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2022-10-10
  • 解决Spring security5.5.7报错Encoded password does not look like BCrypt异常

    解决Spring security5.5.7报错Encoded password does

    这篇文章主要介绍了解决Spring security5.5.7出现Encoded password does not look like BCrypt异常问题,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教
    2024-08-08
  • Java数组与二维数组及替换空格实战真题讲解

    Java数组与二维数组及替换空格实战真题讲解

    数组对于每一门编程语言来说都是重要的数据结构之一,当然不同语言对数组的实现及处理也不尽相同。Java 语言中提供的数组是用来存储固定大小的同类型元素,这篇文章主要介绍了Java数组与二维数组及替换空格实战真题讲解
    2022-07-07
  • 解决Springboot项目中很多页面出现Whitelabel Error Page(404)的问题

    解决Springboot项目中很多页面出现Whitelabel Error Page(404)的问题

    最近在接手的前后端项目中发现其默认路径不是主机+端口(如:http://localhost:3453/)的形式,很多页面的访问是加了一个层级,只要访问页面就会出现Whitelabel Error Page(404),所以本文给大家提供了解决方案,需要的朋友可以参考下
    2024-02-02
  • java设计模式学习之工厂方法模式

    java设计模式学习之工厂方法模式

    这篇文章主要介绍了java设计模式学习之工厂方法模式,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2017-10-10
  • java8新特性之stream的collect实战教程

    java8新特性之stream的collect实战教程

    这篇文章主要介绍了java8新特性之stream的collect实战教程,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧
    2020-08-08

最新评论