Spring 中使用 JDK Timer

在 Java 1.3 以后的版本中,通过 java.util.Timer 和 java.util.TimerTask 这两个类提供了简单的任务调度功能,称之为 Java Timer。Java Timer 允许按照固定频率重复执行某项任务,这比直接通过编写底层线程程序进行任务调度要轻松许多,但是对于诸如“在每周周一8:00执行”这种和日历相关的任务调度需求来说,Java Timer 就无能为力了。

此外,JDK Timer 只适合对执行时间非常短的任务进行调度,因为在 Timer 中所有的 TimerTask 都在同一背景线程中执行,长时间的任务会严重影响到 Timer 的调度工作。所以 Java Timer 的使用范围很窄,在业务需求超过 JavaTimer 的能力范围时,用户应该考虑前面介绍的 Quartz 框架。

1.Timer 和 TimerTask

TimerTask 代表一个需要多次执行的任务,它实现了 Runnable 接口,可以在 run() 方法中定义任务逻辑。而 Timer 负责制定调度规则并调度 TimerTask。

1)TimerTask

TimerTask 相当于 Quartz 中的 Job,代表一个被调度的任务。二者最主要的区别在于,每当执行任务时,Quartz 都会创建一个 Job 实例,而 JDK Timer 则使用相同的 TimerTask 实例。所以,如果 TimerTask 类中拥有状态,那么这些状态对于后面的执行是可见的。从这点上来说,TimerTask 更像 StatefulJob 而非 Job。TimerTask 实现了 Runnable 接口,是一个抽象类,它只有以下3个方法。

(1)abstract void run():子类覆盖这个方法并定义任务执行逻辑,每次执行任务时,run() 方法就被调用一次。

(2)boolean cancel():取消任务。假设任务被安排执行N次,那么在调用该方法后,后续的执行安排将取消。

(3)long scheduledExecutionTime():返回此任务的计划执行时间点。如果在任务执行过程中调用此方法,则返回此次执行所对应的计划执行时间(一个任务的实际执行时间和计划执行时间可能不一致)。该方法一般在 run() 方法内调用,用户可以通过该方法判断本次执行的时间点是否过晚,并据此决定是否要取消本次执行。该方法一般在固定频率执行时使用才会有意义。

2)Timer

Timer 只能以这样的方式对任务进行调度:在延迟一段时间或在指定时间点后运行一次任务或周期性地运行任务。实际上,在 Timer 内部使用 Object#wait(long time) 进行任务的时间调度。这种机制不能保证任务的实时执行,只是一个粗略的近似值。

每个 Timer 对象都有一个对应的“背景线程”,它负责调度并执行 Timer 中所有的 TimerTask。由于所有的 TimerTask 都在这个线程中执行,所以 TimerTask 的执行时间应该比较短。如果一个 TimerTask 的执行占用了过多的时间,那么后面的任务就会受到影响。由于后续任务在调度时间轴上受到了“挤压”,所以可能会造成“扎堆”执行的情况。

当 Timer 中所有的 TimerTask 已经执行完成并且 Timer 对象没有外部引用时,Timer 的任务执行线程才会结束,但这可能需要很长的时间。因此,Timer 在默认情况下使用非守护线程(daemon Thread),这样用户就可以在应用程序中通过 Timer#cancel() 方法手工结束 Timer。如果希望尽快结束 Timer 中的任务,则可以调用 TimerTask#cancel() 方法来达到目的。

提示
Java 有两种线程:用户线程(User Thread)和守护线程(Daemon Thread)。守护线程是指在程序后台运行,提供一种通用服务的线程,垃圾回收线程就是一种典型的守护线程。守护线程是为主程序服务的,因此,当所有的用户线程都结束时,守护线程就会自动终止。因此,也可以将守护线程形象地称为奴仆线程,“主在我存,主亡我死"将线程转换为守护线程可以通过调用 Thread 对象的 setDaemon(true) 方法来实现。

Timer 的构造函数在创建 Timer 对象的同时将启动一个 Timer 背景线程。先来了解一下 Timer 的几个构造函数。

(1)Timer():创建一个Timer,背景线程是非守护线程。

(2)Timer(boolean isDaemon):创建一个 Timer,当 isDaemon 为 true 时,背景线程为守护线程,守护线程将在应用程序主线程停止后自动退出。该方法是 Java 5.0 新增的。

(3)Timer(String name):与 Timer() 类似,只是通过 name 为关联背景线程指定名称。

(4)Timer(Strmg name,boolean isDaemon):与 Timer(boolean isDaemon) 类似并为关联背景线程指定名称。该方法是 Java 5.0 新增的。

通过以下方法执行一次任务。

(1)schedule(TimerTask task, Date time):在特定时间点执行一次任务。

(2)schedule(TimerTask task, long delay):延迟指定时间后执行一次任务,delay的单位为毫秒。

通过以下方法按固定间隔执行任务,间隔时长为上次任务执行完成的时间点到下次任务开始执行的时间点,任务的执行可能产生时间的漂移。

(1)schedule(TimerTask task, Date firstTime, long period):从指定时间点开始周期性地执行任务,period的单位为毫秒,后一次执行将在前一次执行完成后才开始计时。如任务被安排每2秒执行一次,假设第一次任务在0秒时间点开始执行并花费了1.5秒,则第二次将在第3.5秒时执行。

(2)schedule(TimerTask task, long delay, long period):在延迟指定时间后,周期性地执行任务。

通过以下方法按固定频率执行任务。

(1)scheduleAtFixedRate(TimerTask task, Date firstTime, long period):在指定时间点后,以指定频率执行任务。假设一个任务被安排每2秒执行一次,如果第一次执行花费了1.5秒,则在0.5秒后,第二次任务又会开始执行,以保证固定的执行频率。

(2)scheduleAtFixedRate(TimerTask task, long delay, long period):在延迟一段时间后,以指定频率执行任务。

此外,Timer 还拥有几个控制方法。

(1)cancel():取消 Timer 的执行,并丢弃所有被调度的 TimerTask,不过正在执行的任务不受影响。Timer 被取消后,不能调度新的 TimerTask。

(2)purge():将所有已经取消的 TimerTask 从 Timer 列队中清除。如果 TimerTask 没有外部引用,就可以被垃圾回收。一般情况下无须调用该方法,只有在某些特殊情况下,当一次性取消多个 TimerTasks 后,调用该方法才有意义。

3)Java Timer 实例

首先创建一个任务,并在执行10次以后退出,代码如下:

public class SimpleTimerTask  extends TimerTask{
    private int count = 0;
    public void run() {
        System.out.println("execute task.");
        Date exeTime = (new Date(scheduledExecutionTime()));//①获取本次安排执行的时间点
        System.out.println("本次任务安排执行时间点为:"+exeTime);
        if(++count > 5){cancel();}    //②在任务执行10次后主动退出
    }
}

通过扩展 TimerTask 并实现 run() 抽象方法定义一个任务。在 JDK Timer 中没有指定执行特定次数任务的方法。用户可以在任务的 run() 方法中通过自定义代码实现。

下面通过 Timer 以固定延迟时间的方式每5秒执行一次任务。

public class TimerRunner {
    public static void main(String[] args) {
         Timer timer = new Timer();
         TimerTask task = new SimpleTimerTask();
         timer.schedule(task,1000L,1000L);     //①在延迟1秒后,每5秒执行一次任务
    }
}

2.Spring 对 Java Timer 的支持

Spring 在 org.springframework.scheduling.timer 中提供了几个 JDK Timer 的支持类,主要在以下3个方面对 JDK Timer 提供支持。

(1)ScheduledTimerTask,它对 TimerTask 提供封装并提供相关的配置。

(2)通过 MethodInvokingTimerTaskFactoryBean 类可以将一个 Bean 的方法封装为 TimerTask。

(3)通过 TimerFactoryBean 可以更方便地配置 Timer。此外,让 Timer 的生命周期和 Spring 容器的生命周期相关,在初始化 TimerFactoryBean 后启动 Timer,在 Spring 容器关闭前取消 Timer。

1)ScheduledTimerTask

JDK Timer 标准的 API 要求在使用 Timer 方法进行任务调度时才指定调度的规则,

这种方式不太适合进行 Bean 的配置,因此,Spring 提供了 ScheduledTimerTask,通过属性指定任务和调度规则。请看下面的代码:

<bean id="timerTask" class="com.smart.basic.timer.SimpleTimerTask" />

<bean id="scheduledTask"
    class="org.springframework.scheduling.timer.ScheduledTimerTask"
    p:timerTask-ref="timerTask1" //①指定调度任务
    p:delay="10000"  //②延迟时间,单位为毫秒
    p:period="10000" /> //③周期时间,单位为毫秒

如果希望只运行一次任务,则将 period 设置为 0 或负值。在默认情况下,采用固定时间间隔的调度方式,可以通过 fixedRate 属性设置以固定频率的方式执行任务。SimpleTimerTask 还可以将实现了 Runnable 接口的类封装成一个任务,用户可以通过 runnable 属性进行设置。

2)MethodInvokingTimerTaskFactoryBean

类似于 Quartz 的 MethodInvokingJobDetailFactoryBean,Spring 也为 JDK Timer 提供了一个方便类,用于将 Bean 方法封装成一个 TimerTask。

<bean id="myService" class="com.smart.service.MyService" />

<bean id="timerTask1" //①将返回一个TimerTask实例
    class="org.springframework.scheduling.timer.MethodInvokingTimerTaskFactoryBean"
    p:targetObject-ref="myService"  //②业务Bean
    p:targetMethod="doJob"/>    //③业务方法

3)TimerFactoryBean

类似于 Quartz 的 SchedulerFactoryBean,Spring 为 Timer 提供了 TimerFactoryBean 类。用户可以将多个 ScheduledTimerTask 注册到 TimerFactoryBean 中,TimerFactoryBean 将返回一个Timer实例。在 TimerFactoryBean 初始化完成后,对应的 Timer 启动,在 Spring 容器关闭之前,TimerFactoryBean 将取消 Timer。请看下面的配置代码:

<bean id="timer" class="org.springframework.scheduling.timer.TimerFactoryBean">
  <property name="scheduledTimerTasks">
    <list>
      <ref bean="scheduledTask" />
    </list>
  </property>
</bean>

scheduledTimerTasks 属性的类型为 ScheduledTimerTask[],可以注入多个 ScheduledTimerTask。此外, TimerFactoryBean 还拥有一个 daemon 属性,指定生成 Timer 的背景线程是否为守护线程。

原文地址:https://www.cnblogs.com/jwen1994/p/11361410.html

时间: 2024-10-02 16:58:15

Spring 中使用 JDK Timer的相关文章

Spring中的JDK动态代理

Spring中的JDK动态代理 在JDK1.3以后提供了动态代理的技术,允许开发者在运行期创建接口的代理实例.在Sun刚推出动态代理时,还很难想象它有多大的实际用途,现在动态代理是实现AOP的绝好底层技术. JDK的动态代理主要涉及Java.lang.reflect包中的两个类:Proxy和InvocationHandler.其中InvocationHandler是一个接口,可以通过实现该接口定义横切逻辑,并通过反射机制调用目标类的代码,动态将横切逻辑和业务逻辑编织在一起.而Proxy为Invo

Spring + JDK Timer Scheduler Example--reference

http://www.mkyong.com/spring/spring-jdk-timer-scheduler-example/ In this example, you will use Spring’s Scheduler API to schedule a task. 1. Scheduler Task Create a scheduler task… package com.mkyong.common;   public class RunMeTask { public void pri

Spring AOP中的JDK和CGLib动态代理哪个效率更高?

一.背景 今天有小伙伴面试的时候被问到:Spring AOP中JDK 和 CGLib动态代理哪个效率更高? 二.基本概念 首先,我们知道Spring AOP的底层实现有两种方式:一种是JDK动态代理,另一种是CGLib的方式. 自Java 1.3以后,Java提供了动态代理技术,允许开发者在运行期创建接口的代理实例,后来这项技术被用到了Spring的很多地方. JDK动态代理主要涉及java.lang.reflect包下边的两个类:Proxy和InvocationHandler.其中,Invoc

Spring框架中的JDK与CGLib动态代理

JDK和CGLib动态代理区别 JDK动态代理:利用拦截器(拦截器必须实现InvocationHanlder)加上反射机制生成一个实现代理接口的匿名类, 在调用具体方法前调用InvokeHandler来处理. CGLib动态代理:利用ASM开源包,对代理对象类的class文件加载进来,通过修改其字节码生成子类来处理. 何时使用JDK和CGLib: 1)如果目标对象实现了接口,默认情况下会采用JDK的动态代理实现AOP. 2)如果目标对象实现了接口,可以强制使用CGLIB实现AOP. 3)如果目标

JDK和Spring中的设计模式

创建型 1)工厂方法 Collection.iterator() 由具体的聚集类来确定使用哪一个Iterator 2)单例模式 Runtime.getRuntime() 3)建造者模式 StringBuilder 4)原型模式 Java中的Cloneable 结构性 1)适配器模式 InputStreamReader OutputStreamWriter RunnableAdapter 2)装饰器模式 io包 FileInputStream BufferedInputStream 3)代理模式

SSH系列:(28)JDK Timer和Quartz

常见的任务调度有Jdk 的Timer 以及 spring中quartz任务调度框架等. 1.JDK Timer 如果是执行简单的有一定执行周期的,那么使用jdk自带的timer是最简单的. 具体步骤: ①.编写一个简单类继承 TimerTask,在这个新编写的类中重写父类中run方法,在run中执行要执行的操作: ②.编写一个简单类,在类中写一个方法,方法体中使用timer调用在①中创建的类并设置好timer执行周期. MyTask.java package com.rk.test; impor

Spring中的定时调度(Scheduling)和线程池(Thread Pooling)

简介 Spring包含了对定时调度服务的内置支持类.当前,Spring支持从JDK1.3开始内置的Timer类和Quartz Scheduler(http://www.opensymphony.com/quartz/).二者都可以通过FactoryBean,分别指向Timer或Trigger实例的引用进行配置.更进一步,有个对Quartz Scheduler和Timer都有效的工具类可以让你调用某个目标对象的方法(类似通常的MethodInvokingFactoryBean操作).Spring

Spring中使用任务计划(转)

关于在Spring中的任务计划的使用 我今天结合Spring技术手册中的内容,总共总结了5个方面: 1:使用最简单的任务计划,就是继承java.util.TimerTask类,最关键的当然是配置beans-config.xml   文件了,因为我们使用的是spring来管理任务计划.      继承TimerTask类的写法如下: package com;      import java.util.TimerTask;      public class DemoTask extends Ti

Spring中@Transactional事务回滚实例及源码

一.使用场景举例 在了解@Transactional怎么用之前我们必须要先知道@Transactional有什么用.下面举个栗子:比如一个部门里面有很多成员,这两者分别保存在部门表和成员表里面,在删除某个部门的时候,假设我们默认删除对应的成员.但是在执行的时候可能会出现这种情况,我们先删除部门,再删除成员,但是部门删除成功了,删除成员的时候出异常了.这时候我们希望如果成员删除失败了,之前删除的部门也取消删除.这种场景就可以使用@Transactional事物回滚. 二.checked异常和unc