Spring-JDK Timer 以及在Spring(4.0以下)中使用JDK Timer
- 概述
- Timer 和 TimerTask
- 抽象类TimerTask
- Timer
- Timer构造函数及方法
- 示例
- Spring对Java Timer的支持 Spring40已经不支持了推荐使用Quartz
- ScheduledTimerTask
- MethodInvokingTimerTaskFactoryBean
- TimerFactoryBean
- 示例
- 示例源码
概述
在Jdk1.3之后的版本中,通过java.util.Timer和java.util.TimerTask这两个类提供了简单的任务调度功能,称之为Java Timer.
JavaTimer允许按照固定的频率重复执行某项任务,比直接通过线程程序进行任务调度要轻松些,但是对于诸如“每周周一9:00执行”或者和日历相关的任务调度就无能为力了。
此外,JDK Timer只适合对执行时间非常短的任务进行调度,因为在Timer中所有的TimerTask都在同一个背景线程中执行,长时间的任务会影响Timer的调度工作。
如果业务超过Java Timer的能力范围,建议使用Quartz框架。
Timer 和 TimerTask
TimerTask表示一个需要多次执行的任务,它实现了Runnable接口,可以在run方法中定义业务逻辑
Timer负责指定调度规则并调度TimerTask
抽象类TimerTask
TimerTask相当于Quartz中的Job, 代表一个被调度的任务。
二者的区别在于,每当执行任务时,Quartz都会创建一个JOb实例,而Jdk Timer则使用相同的TimerTask实例。 所以如果TimerTask类中拥有状态,那么这些状态对于后面的执行是可见的。 从这一点上来讲,TimerTask更像StatefulJob而非Job。
TimerTask抽象类中只有三个方法
public abstract void run()
子类覆盖这个方法并定义任务的执行逻辑,每次执行任务时,run方法都会被调用一次public boolean cancel()
取消任务,假设任务被安排执行N次,那么在调用该方法后,后续的执行安排将取消public long scheduledExecutionTime()
返回此任务的计划执行时间点。该方法一般在run方法内部调用,用户可以通过该方法判断本次执行的时间点是否过晚,并据此决定是否需要取消本次执行。该方法一般在固定执行频率执行时才有意义。
Timer
Timer只能以如下方式对任务进行调度:在延迟一段时间或者在指定时间点后运行一次任务或周期性的运行任务.
实际上,在Timer内部使用Object#wait(long time)进行任务的时间调度。这种机制不能保证任务的实时执行,只是一个粗略的近似值。
每个Timer对象那个都有一个对应的“背景线程”,它负责调度并执行Timer中所有的TimerTask。 由于所有的TimerTask都在这线程中执行,所以TimerTask的执行时间应该比较短。 如果一个TimerTask的执行占用了过多的时间,后面的任务就会受到影响。 由于后续任务在调度时间上受到了“挤压”,所以可能会造成扎堆执行的情况。
当Timer中所有的TimerTask已经执行完成并且Timer对象没有外部引用时,Timer的任务执行线程才回结束,但这可能需要很长的时间。 因此Timer在默认情况下适用daemon Thread,这样用户就可以在应用程序中通过Time#cancel方法手工结束Timer.
如果希望尽快结束Timer中的任务,则可以调用TimerTask#cancel方法手工达到目的。
Java有两种线程:用户线程(User Thread)和守护线程(Daemon Thread).
守护线程是在在程序后台运行,提供了一种通用服务线程,垃圾回收线程就是一种典型的守护线程。
守护线程是为主线程服务的,因此当所有的用户线程都结束时,守护线程会自动终止,因此,也可以将守护线程形象的称为奴仆线程,“主存我存,主亡我死”
将线程转换为守护线程可以通过Thread对象的setDaemon(true)方法来实现
Timer构造函数及方法
Timer的构造函数在创建Timer对象的同时将启动一个Timer背景线程。
我们先来看下Timer的构造函数
public Timer():创建一个Timer,背景线程是非守护线程
public Timer(String name):与Timer类似,只是通过name为关丽娜背景线程指定名称
public Timer(boolean isDaemon) :创建一个Timer,当isDaemon为true时,背景线程为守护线程,守护线程将在应用程序主线程停止后自动退出。该方法为Java5.0新增的。
public Timer(String name, boolean isDaemon):与Timer(boolean isDaemon) 相似并为关联背景线程指定名称,该方法是5.0新增的
通过以下方法执行一次任务
public void schedule(TimerTask task, Date time) 在特定时间点执行一次任务
public void schedule(TimerTask task, long delay) 延迟指定时间后执行一次任务,delay的单位为毫秒
通过如下方法按照固定间隔执行任务,间隔时长为上次任务执行完成的时间点到下次任务开始执行的时间点,任务的执行可能产生时间的漂移
public void schedule(TimerTask task, Date firstTime, long period)从指定时间点开始周期性的执行任务,period的单位为毫秒,后一次执行将在前一次执行完成后开始计时。 比如任务安排每2S执行一次,假设第一次任务在0秒时间点开始执行并花费了1.5S , 这第二次将在第3.5秒时执行
public void schedule(TimerTask task, long delay, long period) 在延迟执行的时间后,周期性的执行任务
通过以下方法按照固定的频率执行任务
public void scheduleAtFixedRate(TimerTask task, Date firstTime,
long period) 在指定时间点后,以指定频率执行任务。 假设一个任务被安排每2S执行一次,如果第一次执行花费了1.5S , 则在0.5S后,第二次任务开始执行,以保证固定的执行频率public void scheduleAtFixedRate(TimerTask task, long delay, long period) 在延迟一段时间后,以指定频率执行任务。
此外Timer还有几个控制方法
cancel :取消Timer的执行,并丢弃所有被调度的TimerTask不过正在执行的不受影响。 Timer被取消后,不能调度新的TimerTask
purge:将所有已经取消的TimerTask从Timer队列中清除。 如果TimerTask没有外部引用,那就可以被垃圾回收。 一般情况下无须调用该方法,只有在某些特定情况下,当一次性取消多个TimerTask后,调用该方法才有意义
示例
Job
package com.xgj.quartz.jdkTimer.jdkTimer;import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.TimerTask;/*** * * @ClassName: MyTask* * @Description: 任务执行10次后退出* * @author: Mr.Yang* * @date: 2017年11月17日 下午5:01:32*/
public class MyTask extends TimerTask {int count = 0;@Overridepublic void run() {SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");System.out.println("Task begins to execute,execute times:" + count);Date date = new Date(scheduledExecutionTime());System.out.println("本次任务执行时间点为:" + sdf.format(date));// 执行10次后退出if (++count > 10) {cancel();System.out.println("Task exits");}}}
调度类
package com.xgj.quartz.jdkTimer.jdkTimer;import java.util.Timer;public class TimerRunner {public static void main(String[] args) {Timer timer = new Timer();MyTask myTask = new MyTask();// 延迟1秒,每5S执行一次任务timer.schedule(myTask, 1000L, 5000L);}
}
运行结果
Task begins to execute,execute times:0
本次任务执行时间点为:2017-11-18 12:35:55
Task begins to execute,execute times:1
本次任务执行时间点为:2017-11-18 12:36:00
Task begins to execute,execute times:2
本次任务执行时间点为:2017-11-18 12:36:05
Task begins to execute,execute times:3
本次任务执行时间点为:2017-11-18 12:36:10
Task begins to execute,execute times:4
本次任务执行时间点为:2017-11-18 12:36:15
Task begins to execute,execute times:5
本次任务执行时间点为:2017-11-18 12:36:20
Task begins to execute,execute times:6
本次任务执行时间点为:2017-11-18 12:36:25
Task begins to execute,execute times:7
本次任务执行时间点为:2017-11-18 12:36:30
Task begins to execute,execute times:8
本次任务执行时间点为:2017-11-18 12:36:35
Task begins to execute,execute times:9
本次任务执行时间点为:2017-11-18 12:36:40
Task begins to execute,execute times:10
本次任务执行时间点为:2017-11-18 12:36:45
Task exits
Spring对Java Timer的支持 (Spring4.0+已经不支持了,推荐使用Quartz)
Spring在org.springframework.scheduling.timer中提供了几个JDK Timer的支持类,主要在以下3个方面:
ScheduledTimerTask对TimerTask提供封装并提供相关的配置
通过MethodInvokingTimerTaskFactoryBean类可以将一个Bean的方法封装为TimerTask
通过TimerFactoryBean可以更方便的配置Timer。此外,让Timer的生命周期和Spring容器的生命周期相关,在初始化TimerFactory后启动Timer,在Spring容器关闭前取消Timer
ScheduledTimerTask
JDKTimer标准的API要求在使用Timer方法进行任务调度时才指定调度规则,不符合Bean的配置,Spring为此提供了ScheduledTimerTask,通过属性指定任务和调度规则。
<bean id="scheduleTask" class="org.springframework.scheduling.timer.ScheduledTimerTask"><property name="timerTask" ref="timeTask" /><!--指定调度任务 --><property name="delay" value="1000" /> <!--延迟时间,单位为毫秒 --><property name="period" value="1000" /> <!--周期时间,单位为毫秒 --></bean>
如果希望只运行一次,则将period设置为0或者是负值。
默认情况下,采用固定时间间隔的调度方式,可以通过fixRate属性设置以固定频率的方式执行任务。
SimpleTimerTask还可以将实现了Runnable接口的类封装成一个任务,用户可以通过runnable属性进行设置。
MethodInvokingTimerTaskFactoryBean
类似Quartz的MethodInvokingJobDetailFactoryBean,Spring也为 JDK Timer提供了一个方便类,用于将Bean封装成一个TimerTask.
使用Spring 时,我们并不一定要继承TimerTask 来定义一个任务,Spring 提供 org.springframework.scheduling.timer.MethodInvokingTimerTaskFactoryBean,如下所示
package com.xgj.quartz.jdkTimer.springJdkTimer2;import java.text.SimpleDateFormat;
import java.util.Date;public class MyJobService {public void doJob() {SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");System.out.println("本次任务执行时间点为:" + sdf.format(new Date()));}}
配置文件
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://www.springframework.org/schema/beanshttp://www.springframework.org/schema/beans/spring-beans.xsd"><bean id="myJobService" class="com.xgj.quartz.jdkTimer.springJdkTimer2.MyJobService" /><!-- Spring4.0以上版本已经不支持ScheduledTimerTask,而是推荐使用Quartz --><bean id="scheduleTask" class="org.springframework.scheduling.timer.MethodInvokingTimerTaskFactoryBean"><property name="targetObject" ref="myJobService" /><property name="targetMethod" value="doJob" /> </bean><bean id="scheduledTimerTask" class="org.springframework.scheduling.timer.ScheduledTimerTask"> <property name="timerTask" ref="scheduleTask"/> <property name="period" value="5000"/> <property name="delay" value="1000"/> </bean> <bean id="timerFactory" class="org.springframework.scheduling.timer.TimerFactoryBean"><property name="scheduledTimerTasks"><list><ref bean="scheduledTimerTask" /></list></property></bean></beans>
TimerFactoryBean
类似于Quartz的SchedulerFactoryBean,Spring为Timer提供了TimerFactoryBean类。 用户可以将多个ScheduledTimerTask注册到TimerFactoryBean中,TimerFactoryBean将返回一个Timer实例。 在初始化TimerFactory后启动Timer,在Spring容器关闭前取消Timer
<bean id="timerFactory" class="org.springframework.scheduling.timer.TimerFactoryBean"><property name="scheduledTimerTasks"><list><ref bean="scheduleTask" /></list></property></bean>
scheduledTimerTasks的属性类型为 ScheduledTimerTask[]可以注入多个ScheduledTimerTask .
此外TimerFactoryBean还有个daemon属性,指定生成的Timer的背景线程是否是守护线程。
示例
为了演示该功能,我们需要先引入4.0以下的Spring依赖,这里我们使用
修改pom.xml中的如下信息
<spring.version>3.2.18.RELEASE</spring.version>
官方说明:
https://docs.spring.io/spring/docs/3.0.x/javadoc-api/org/springframework/scheduling/timer/ScheduledTimerTask.html
Job
package com.xgj.quartz.jdkTimer.springJdkTimer;import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.TimerTask;/*** * * @ClassName: MyTask* * @Description: 任务执行10次后退出* * @author: Mr.Yang* * @date: 2017年11月17日 下午5:01:32*/
public class MyTask extends TimerTask {int count = 0;@Overridepublic void run() {SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");System.out.println("Task begins to execute,execute times:" + count);Date date = new Date(scheduledExecutionTime());System.out.println("本次任务执行时间点为:" + sdf.format(date));// 执行10次后退出if (++count > 10) {cancel();System.out.println("Task exits");}}}
配置文件
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://www.springframework.org/schema/beanshttp://www.springframework.org/schema/beans/spring-beans.xsd"><bean id="timeTask" class="com.xgj.quartz.jdkTimer.springJdkTimer.MyTask" /><!-- Spring4.0以上版本已经不支持ScheduledTimerTask,而是推荐使用Quartz --><bean id="scheduleTask" class="org.springframework.scheduling.timer.ScheduledTimerTask"><property name="timerTask" ref="timeTask" /><property name="delay" value="1000" /> <!--延迟1s --><property name="period" value="1000" /> <!--1s一次 --><property name="fixedRate" value="true" /></bean><bean id="timerFactory" class="org.springframework.scheduling.timer.TimerFactoryBean"><property name="scheduledTimerTasks"><list><ref bean="scheduleTask" /></list></property></bean></beans>
测试类(这种定时任务,写单元测试只会执行一次,这里我们写个简单的main方法)
package com.xgj.quartz.jdkTimer.springJdkTimer;import org.springframework.context.support.ClassPathXmlApplicationContext;public class SpringJdkTimerTest {public static void main(String[] args) {ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext("classpath:com/xgj/quartz/jdkTimer/springJdkTimer/spring-jdkTimer.xml");System.out.println("initContext successfully");MyTask task = ctx.getBean("timeTask", MyTask.class);task.run();}}
运行结果
2017-11-18 12:25:20,564 INFO [main] (AbstractApplicationContext.java:518) - Refreshing org.springframework.context.support.ClassPathXmlApplicationContext@88b6cca: startup date [Sat Nov 18 12:25:20 BOT 2017]; root of context hierarchy
2017-11-18 12:25:20,658 INFO [main] (XmlBeanDefinitionReader.java:316) - Loading XML bean definitions from class path resource [com/xgj/quartz/jdkTimer/springJdkTimer/spring-jdkTimer.xml]
2017-11-18 12:25:21,084 INFO [main] (DefaultListableBeanFactory.java:605) - Pre-instantiating singletons in org.springframework.beans.factory.support.DefaultListableBeanFactory@381702ff: defining beans [timeTask,scheduleTask,timerFactory]; root of factory hierarchy
2017-11-18 12:25:21,230 INFO [main] (TimerFactoryBean.java:97) - Initializing Timer
initContext successfully
Task begins to execute,execute times:0
本次任务执行时间点为:2017-11-18 12:25:21
Task begins to execute,execute times:1
本次任务执行时间点为:2017-11-18 12:25:22
Task begins to execute,execute times:2
本次任务执行时间点为:2017-11-18 12:25:23
Task begins to execute,execute times:3
本次任务执行时间点为:2017-11-18 12:25:24
Task begins to execute,execute times:4
本次任务执行时间点为:2017-11-18 12:25:25
Task begins to execute,execute times:5
本次任务执行时间点为:2017-11-18 12:25:26
Task begins to execute,execute times:6
本次任务执行时间点为:2017-11-18 12:25:27
Task begins to execute,execute times:7
本次任务执行时间点为:2017-11-18 12:25:28
Task begins to execute,execute times:8
本次任务执行时间点为:2017-11-18 12:25:29
Task begins to execute,execute times:9
本次任务执行时间点为:2017-11-18 12:25:30
Task begins to execute,execute times:10
本次任务执行时间点为:2017-11-18 12:25:31
Task exits
示例源码
代码已托管到Github—> https://github.com/yangshangwei/SpringMaster
Spring-JDK Timer 以及在Spring(4.0以下)中使用JDK Timer相关推荐
- spring swing_使用Swing的Spring简介
在你开始前 关于本教程 本教程将帮助您学习使用Swing和依赖项注入 (也称为控制反转(IOC))进行应用程序开发的Spring框架的基础. 在简要概述了Spring和依赖项注入之后,本教程的大部分内 ...
- 在Spring中使用JDK定时器实现调度任务
在Spring中使用JDK定时器实现调度任务 作者:chszs,转载需注明.博客主页: http://blog.csdn.net/chszs 本文探讨Spring如何集成JDK的Timer定时器,实现 ...
- docker image设置jdk版本_Docker 部署 Spring Boot
作者:云天 镜像下载.域名解析.时间同步请点击 阿里巴巴开源镜像站 一.Docker 简介 Docker 属于 Linux 容器的一种封装,提供简单易用的容器使用接口.它是目前最流行的 Linux 容 ...
- Spring Framework 5.0.0.M4中文文档第3章
文章目录 Part II. 核心技术 3. IoC容器 3.2 容器概述 3.2.1 配置元数据 3.2.2 实例化容器 3.2.3 使用容器 3.3 Bean概述 3.3.1 命名bean 3.3. ...
- (转)面试必备技能:JDK动态代理给Spring事务埋下的坑!
一.场景分析 最近做项目遇到了一个很奇怪的问题,大致的业务场景是这样的:我们首先设定两个事务,事务parent和事务child,在Controller里边同时调用这两个方法,示例代码如下: 1.场景A ...
- Spring XD 1.1 M2 and 1.0.3 released---support kafka
官方地址:http://spring.io/blog/2014/12/23/spring-xd-1-1-m2-and-1-0-3-released On behalf of the Spring XD ...
- Spring Framework 5.1.6、5.0.13 与 4.3.23 发布
开发四年只会写业务代码,分布式高并发都不会还做程序员? Spring Framework 5.1.6.5.0.13 与 4.3.23 发布了. v5.1.6 包含 49 个 bug 修复和功能改进 ...
- Spring Security系列(11)- Security5.0版本Oauth2开放平台环境搭建
前言 上篇文档,我们了解了OAuth2.0的相关知识,接下来我们搭建一个自己的Oauth2开放平台. 从流程图中,可以看到,后台需要搭建一个认证服务器,负责用户登录.第三方授权等功能,还需要搭建自己的 ...
- 8. Spring Security 5.1之 OAuth 2.0 Login
1.OAuth 2.0 Login OAuth 2.0登录功能为应用程序提供了使用OAuth 2.0提供程序(例如GitHub)或OpenID Connect 1.0提供程序(例如Google)上的现 ...
最新文章
- 精选SpringBoot+Vue开发的开源系统(前端+后端+小程序)
- Git命令集十五——拉取命令
- r语言 plot_R和Python的特点对比,这样你就知道该怎么选择了
- 跨服务器上传文件方式
- 用原生JavaScript实现淡入淡出轮播图
- ASP.NET Core学习资源汇总
- 华为内部面试题库---(6)
- 程序员精美简历Top榜—面试必备
- python打包成exe导入文件_【转载】将python脚本打包成exe文件
- springboot 日志_Springboot与日志
- linux vino vnc,Ubuntu下包含2种远程桌面的方式:VINO-Server以及VNC Server。 .
- date time 和string
- ecshop实现弹出登录框
- mysql分库分表 mycat_你们要的MyCat实现MySQL分库分表来了
- JMeter的取样器
- 【Win10如何给桌面文件夹自定义图标】怎么改应用图标1.回收站,垃圾桶图标改为pop猫+2.如何把icon图标打包成dll然后在修改文件夹图标时在该dll中选择,用Resource Hacker
- 线性代数1.5 克莱姆法则
- 感慨一下中年人的焦虑
- 每个程序员书柜必有的编程书籍
- 215868-33-0,Ocean Blue, SE,3-羧基-6,8-二氟-7-羟基香豆素琥珀酰亚胺酯蓝色荧光染料