浅谈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的使用方法代码示例

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

(0)

相关推荐

  • RxJava2.x实现定时器的实例代码

    前言 由于现在网络层已经升级到RxJava2.x相关的了,所以需要做些调整.虽然RxJava1.x和RxJava2.x同属RxJava系列,但由于RxJava2.x部分代码的重写,导致RxJava2.x与RxJava1.x已是两个不同的版本,RxJava2.x在性能上更优,尤其在背压支持上.当然,此篇重点不在Rx版本上的区别,有兴趣的同学可以自行研究.当然,2.x之于1.x的区别之一是2.x中已经没有 Subscription mSubscription, Observable.create()

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

    前言 定时/计划功能在Java应用的各个领域都使用得非常多,比方说Web层面,可能一个项目要定时采集话单.定时更新某些缓存.定时清理一批不活跃用户等等.定时计划任务功能在Java中主要使用的就是Timer对象,它在内部使用多线程方式进行处理,所以它和多线程技术关联还是相当大的.那和ThreadLocal一样,还是先讲原理再讲使用,Timer的实现原理不难,就简单扫一下就好了. Timer的schedule(TimeTask task, Date time)的使用 该方法的作用是在执行的日期执行一

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

    定时的功能我们在手机上见得比较多,比如定时清理垃圾,闹钟,等等.定时功能在java中主要使用的就是Timer对象,他在内部使用的就是多线程的技术. Time类主要负责完成定时计划任务的功能,就是在指定的时间的开始执行某个任务. Timer类的作用是设置计划任务,而封装任务内容的类是TimerTask类.此类是一个抽象类,继承需要实现一个run方法. 利用java制作定时器比较简单,有现成的接口帮助实现.java中制作定时器使用的是Timer和TimerTask,是util包的.java.util

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

    1.首先肯定是容器一启动就要启动定时器,所以我们可以选择把定时器写在一个监听器里,容器一启动所以监听器也就跟着启动,然后定时器就可以工作了. 第一步,把自己写的监听器加到web.xml中: 第二步,写一个监听器,实现ServletContextListener接口: 第三步,写一个定时器,继承TimerTask,在复写的run()方法里写具体的业务逻辑. 第四步,在自己的监听器里复写的 public void contextInitialized(ServletContextEvent arg0

  • Java 中Timer和TimerTask 定时器和定时任务使用的例子

    这两个类使用起来非常方便,可以完成我们对定时器的绝大多数需求 Timer类是用来执行任务的类,它接受一个TimerTask做参数 Timer有两种执行任务的模式,最常用的是schedule,它可以以两种方式执行任务:1:在某个时间(Data),2:在某个固定的时间之后(int delay).这两种方式都可以指定任务执行的频率 TimerTest.Java: package com.cn; import java.io.IOException; import java.util.Timer; pu

  • Java定时器例子_动力节点Java学院整理

    Timer类是用来执行任务的类,它接受一个TimerTask做参数 ,这两个类使用起来非常方便,可以完成我们对定时器的绝大多数需求 . Timer有两种执行任务的模式,最常用的是schedule,它可以以两种方式执行任务: 1:在某个时间(Data), 2:在某个固定的时间之后(int delay).这两种方式都可以指定任务执行的频率 TimerTest.Java: package com.bjpowernode.test; import java.io.IOException; import

  • Java定时器问题实例解析

    定时器问题 定时器属于基本的基础组件,不管是用户空间的程序开发,还是内核空间的程序开发,很多时候都需要有定时器作为基础组件的支持.一个定时器的实现需要具备以下四种基本行为:添加定时器.取消定时器.定时器检查.到期执行. 请设计一个定时器并实现以下三种基本行为,函数原型已给出,可使用任意编程语言设计数据结构及实现,并尽可能高效地支持大量定时器: // 添加定时器:经过特定时间间隔后执行目标操作 // 输入 1:Interval 定时器时间,单位ms // 输入 2:ExpiryAction 目标操

  • Java定时器Timer使用方法详解

    一.概念 定时计划任务功能在Java中主要使用的就是Timer对象,它在内部使用多线程的方式进行处理,所以它和多线程技术还是有非常大的关联的.在JDK中Timer类主要负责计划任务的功能,也就是在指定的时间开始执行某一个任务,但封装任务的类却是TimerTask类. 通过继承 TimerTask 类 并实现 run() 方法来自定义要执行的任务: public class Mytask extends TimerTask { @Override public void run() { DateF

  • 浅谈java定时器的发展历程

    在开发中,我们经常需要一些周期性的操作,例如每隔几分钟就进行某一项操作.这时候我们就要去设置个定时器,Java中最方便.最高效的实现方式是用java.util.Timer工具类,再通过调度java.util.TimerTask任务. Timer是一种工具,线程用其安排以后在后台线程中执行的任务.可安排任务执行一次,或者定期重复执行.实际上是个线程,定时调度所拥有的TimerTasks. TimerTask是一个抽象类,它的子类由Timer安排为一次执行或重复执行的任务.实际上就是一个拥有run方

  • 浅谈Java文件被执行的历程

    Java的编译过程 Java程序从源文件创建到程序运行要经过两大步骤 1.源文件由编译器编译成字节码(ByteCode) 2.字节码由java虚拟机解释运行.因为java程序既要编译同时也要经过JVM的解释运行,所以说Java被称为半解释语言("semi-interpreted" language). 一个例子 下面我们通过一个简单的 OneTest.java,来看一下一个java文件从编译到运行的历程. Public class OneTest{ public static void

  • 浅谈JAVA Actor模型的一致性与隔离性

    一.Actor模型介绍 在单核 CPU 发展已经达到一个瓶颈的今天,要增加硬件的速度更多的是增加 CPU 核的数目.而针对这种情况,要使我们的程序运行效率提高,那么也应该从并发方面入手.传统的多线程方法又极其容易出现 Bug 而难以维护,不过别担心,今天将要介绍另一种并发的模式能一定程度解决这些问题,那就是 Actor 模型. Actor 模型其实就是定义一组规则,这些规则规定了一组系统中各个模块如何交互及回应.在一个 Actor 系统中,Actor 是最小的单元模块,系统由多个 Actor 组

  • 浅谈Java并发编程之Lock锁和条件变量

    简单使用Lock锁 Java 5中引入了新的锁机制--java.util.concurrent.locks中的显式的互斥锁:Lock接口,它提供了比synchronized更加广泛的锁定操作.Lock接口有3个实现它的类:ReentrantLock.ReetrantReadWriteLock.ReadLock和ReetrantReadWriteLock.WriteLock,即重入锁.读锁和写锁.lock必须被显式地创建.锁定和释放,为了可以使用更多的功能,一般用ReentrantLock为其实例

  • 浅谈Java响应式系统

    初识响应式系统 ReactiveX的本质就是Observer+Iterator+函数编程+异步.是一个事件驱动的,异步的,可观察的序列. 使用RxJava可以将异步的回调改写成为链式调用.在代码上看起来非常简洁明了.当然JDK也提供了CompletionStage提供了类似的解决回调的功能. Rxjava只是一个java的基本库,如果我们想要构建响应式的服务器,响应式的web,响应式的数据访问,甚至是响应式的微服务,又该如何处理呢? 这个时候我了解到了Vert.x.Vert.x就是用来构建Rea

  • 浅谈Java中Unicode的编码和实现

    Unicode的编码和实现 大概来说,Unicode编码系统可分为编码方式和实现方式两个层次. 编码方式 字符是抽象的最小文本单位.它没有固定的形状(可能是一个字形),而且没有值."A"是一个字符,"€"也是一个字符.字符集是字符的集合.编码字符集是一个字符集,它为每一个字符分配一个唯一数字. Unicode 最初设计是作为一种固定宽度的 16 位字符编码.也就是每个字符占用2个字节.这样理论上一共最多可以表示216(即65536)个字符.上述16位统一码字符构成基

  • 浅谈java常量池

    java常量池技术 java中常量池技术说的通俗点就是java级别的缓存技术,方便快捷的创建一个对象.当需要一个对象时,从池中去获取(如果池中没有,就创建一个并放入池中),当下次需要相同变量的时候,不用重新创建,从而节省空间. java八种基本类型的包装类和对象池 java中的基本类型的包装类.其中Byte.Boolean.Short.Character.Integer.Long实现了常量池技术,(除了Boolean,都只对小于128的值才支持) 比如,Integer对象 Integer i1

  • 浅谈Java工程读取resources中资源文件路径的问题

    正常在Java工程中读取某路径下的文件时,可以采用绝对路径和相对路径,绝对路径没什么好说的,相对路径,即相对于当前类的路径.在本地工程和服务器中读取文件的方式有所不同,以下图配置文件为例. 本地读取资源文件 java类中需要读取properties中的配置文件,可以采用文件(File)方式进行读取: File file = new File("src/main/resources/properties/basecom.properties"); InputStream in = new

  • 浅谈Java多线程处理中Future的妙用(附源码)

    java 中Future是一个未来对象,里面保存这线程处理结果,它像一个提货凭证,拿着它你可以随时去提取结果.在两种情况下,离开Future几乎很难办.一种情况是拆分订单,比如你的应用收到一个批量订单,此时如果要求最快的处理订单,那么需要并发处理,并发的结果如果收集,这个问题如果自己去编程将非常繁琐,此时可以使用CompletionService解决这个问题.CompletionService将Future收集到一个队列里,可以按结果处理完成的先后顺序进队.另外一种情况是,如果你需要并发去查询一

  • 浅谈java中null是什么,以及使用中要注意的事项

    1.null既不是对象也不是一种类型,它仅是一种特殊的值,你可以将其赋予任何引用类型,你也可以将null转化成任何类型,例如: Integer i=null; Float f=null; String s=null; 但是不能把null赋值给基本类型,如int ,float,double等 int k=null ----------编译器会报错cannot convert from null to int 2.null是关键字,像public.static.final.它是大小写敏感的,你不能将

随机推荐