工作中经常接触到定时任务,实现定时任务的方式很多,常见的有 Spring @schedule 注解配合 Cron 表达式、JDK 自带的 TimerTask or Timer、使用开源作业调度框架 Quartz、线程池 ScheduleExecutorService 和其实现类 ScheduledThreadPoolExecutor。
@schedule 注解
Spring 中的 @schedule 是我最早接触学会的,注解实现非常方便,注解中有三个参数分别是 Cron 表达式、fixedRate 和 initialDelay,具体使用看你的场景,需要注意的是使用 @schedule 注解的方法的类必须用 @Component (最好)或者 @Service @Controller 等注解进行修饰,将其交给 Spring 进行托管,且在 XML 配置文件中必须有相应的配置。随着学习的深入,后来了解到 @schedule 内部本质上是基于 Quartz 实现的。Quartz 是一个任务调度框架,@schedule 注解修饰的方法会被交给 ScheduledAnnotationBeanPostProcessor 处理,在 ScheduledAnnotationBeanPostProcessor 类中会解析 @schedule 中的三个参数,根据参数的不同,将任务交给 ScheduledTaskRegistrar 进行处理,3种不同属性的task均由quartz的taskScheduler的不同方法来完成,scheduleWithFixedDelay、
scheduleAtFixedRate 和 schedule。
Timer & TimerTask
Timer 是 JDK 中提供的一个定时器工具,使用的时候会在主线程之外起一个单独的线程执行指定的计划任务,可以指定执行一次或者反复执行多次。TimerTask 是一个实现了 Runnable 接口的抽象类,代表一个可以被 Timer 执行的任务。我们可以这样理解 Timer 是一种定时器工具,用来在一个后台线程计划执行指定任务,而 TimerTask 是一个抽象类,它的子类代表一个可以被 Timer 计划的任务。
TimerTask 我自己极少使用,主要是它有一些弊端,Timer 在执行定时任务时只会创建一个线程,所以如果存在多个任务,且任务时间过长,超过了两个任务的间隔时间,那么第二个任务只能等待第一个任务执行完毕后再执行,其行为可能已经不符合编码者期望的了。例如 t2 定义 3s 后开始执行,t1 定义在 1s 后执行,但是 t1 执行过程总共消耗了 4s,那么实际上 t2 会在第 6s 才会执行,因为 Timer 是单线程的缘故。
package com.zhy.concurrency.Timer;
import java.util.Timer;
import java.util.TimerTask;
public class TimerTest
{
private static long start;
public static void main(String[] args) throws Exception{
TimerTask task1 = new TimerTask(){