码迷,mamicode.com
首页 > 编程语言 > 详细

Spring 中使用 JDK Timer

时间:2019-08-18 15:28:14      阅读:79      评论:0      收藏:0      [点我收藏+]

标签:wait   uart   runnable   ext   edr   扩展   rate   rac   启动   

在 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 的背景线程是否为守护线程。

Spring 中使用 JDK Timer

标签:wait   uart   runnable   ext   edr   扩展   rate   rac   启动   

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

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!