likes
comments
collection
share

SpringBoot 定时调度 : 从Timer到Quartz

作者站长头像
站长
· 阅读数 15

Java 中常用的定时调度框架有以下几种:

  1. Timer:Java 标准库中提供的一个定时调度工具,可以在指定的时间点或时间间隔内执行任务。Timer 的缺点是不支持并发执行和错误处理机制较弱。
  2. ScheduledExecutorService:Java 标准库中提供的另一个定时调度工具,也可以在指定的时间点或时间间隔内执行任务。与 Timer 相比,ScheduledExecutorService 支持并发执行和错误处理机制较强。
  3. Spring Task:Spring 框架中提供的一个定时调度工具,可以用于执行定时任务、异步任务等。Spring Task 支持多种时间表达式、动态调整和错误处理机制。
  4. Quartz:一个用于定时调度的开源框架,可以在指定的时间点或时间间隔内自动执行任务。Quartz 支持多种配置方式和调度策略,功能丰富、灵活性高、易于集成并且可靠性较高。

Timer

Timer 是 Java 标准库中提供的一个定时调度工具,可以在指定的时间点或时间间隔内执行任务。Timer 提供了多种调度方式和执行策略,可以用于管理简单的定时任务。

Timer 使用 TimerTask 类来表示要执行的任务,TimerTask 是一个抽象类,需要继承并实现 run() 方法来定义具体的任务逻辑。Timer 还提供了多种调度方式,包括定时调度、周期性调度等。

以下是一个简单的 Timer 示例:

import java.util.Timer;
import java.util.TimerTask;

public class MyTask extends TimerTask {
    public void run() {
        System.out.println("定时任务执行了!");
    }

    public static void main(String[] args) {
        Timer timer = new Timer();
        MyTask task = new MyTask();
        timer.schedule(task, 5000); // 5秒后执行任务
    }
}

在上述代码中,我们定义了一个名为 MyTask 的 TimerTask 类,并在其中实现了 run() 方法来定义具体的任务逻辑。然后,在 main() 方法中创建了一个 Timer 实例,并使用 schedule() 方法来指定任务的执行时间和执行方式。

需要注意的是,Timer 的缺点是不支持并发执行和错误处理机制较弱。如果需要实现复杂的定时任务,可以考虑使用其它的定时调度框架,例如 Quartz、ScheduledExecutorService 等。

ScheduledExecutorService

ScheduledExecutorService 基于 Executor 框架,使用 ThreadPoolExecutor 来实现任务的执行和管理。

好的,下面是一个使用 ScheduledExecutorService 实现每天凌晨1点执行任务的示例:

import java.time.LocalTime;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;

public class MyTask implements Runnable {
    public void run() {
        System.out.println("定时任务执行了!当前时间:" + LocalTime.now());
    }

    public static void main(String[] args) {
        ScheduledExecutorService executor = Executors.newScheduledThreadPool(1);
        MyTask task = new MyTask();
        long initialDelay = calculateInitialDelay();
        executor.scheduleAtFixedRate(task, initialDelay, 24, TimeUnit.HOURS); // 每天凌晨1点执行任务
    }

    private static long calculateInitialDelay() {
        LocalTime now = LocalTime.now();
        LocalTime target = LocalTime.of(1, 0, 0); // 目标执行时间为每天凌晨1点
        if (now.isBefore(target)) {
            return Duration.between(now, target).toMillis();
        } else {
            return Duration.between(now, target.plusDays(1)).toMillis();
        }
    }
}

在上述代码中,我们定义了一个名为 MyTask 的 Runnable 类,并在其中实现了 run() 方法来定义具体的任务逻辑。然后,在 main() 方法中创建了一个 ScheduledExecutorService 实例,并使用 scheduleAtFixedRate() 方法来指定任务的执行时间和执行方式。

为了实现每天凌晨1点执行任务,我们先定义了一个 calculateInitialDelay() 方法来计算任务的初始延迟时间。该方法会根据当前时间和目标执行时间计算出初始延迟时间,如果当前时间早于目标执行时间,则将初始延迟时间设置为两者时间差,否则将初始延迟时间设置为两者时间差加上一天的时间差。

最后,我们使用 scheduleAtFixedRate() 方法来指定任务的执行时间和执行周期,其中 initialDelay 参数为初始延迟时间,period 参数为执行周期,这里是每24小时执行一次。

需要注意的是,为了获取当前时间和计算时间差,我们使用了 Java 8 中的 LocalDate、LocalTime 和 Duration 类。如果使用的是 Java 7 或更早的版本,可以使用 Date、Calendar 等类来代替。

Spring Task

Spring Task(也称为 Spring Scheduler)是 Spring 框架中提供的定时任务框架,可以在指定的时间点或时间间隔内执行任务。Spring Task 封装了 Java 标准库中的 Timer 和ScheduledExecutorService,提供了更加简单和方便的定时任务管理方式。

import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;

@Component
public class MyTask {
    @Scheduled(cron = "0 0 1 * * ?") // 每天凌晨1点执行任务
    public void execute() {
        System.out.println("定时任务执行了!当前时间:" + new Date());
    }
}

在上述代码中,我们定义了一个名为 MyTask 的类,并在其中使用 @Scheduled 注解来指定任务的执行时间和执行方式。这里使用了 cron 表达式来指定每天凌晨1点执行任务。

@Scheduled注解支持多种参数设置,例如:

  • fixedRate:表示每隔多少毫秒执行一次。
  • fixedDelay:表示延迟多少毫秒后执行。
  • initialDelay:表示启动延迟多少毫秒后执行第一次。
  • cron:使用Cron表达式来指定执行时间,例如“0 0 12 * * ?”表示每天中午12点执行。

需要注意的是,@Scheduled注解只能用于Spring容器中的Bean中,因此需要将使用该注解的类标注为@Component或其他的Spring组件注解。

cron表达式

@Scheduled 注解使用 cron 表达式来指定任务执行的时间,cron 表达式由6个或7个字段组成,分别表示秒、分、时、日、月、周、年(可选)。

任务执行时间cron 表达式
每分钟执行一次0 * * * * ?
每小时的第30分钟执行一次0 30 * * * ?
每天的凌晨1点执行一次0 0 1 * * ?
每周的周日凌晨1点执行一次0 0 1 ? * SUN
每月的1号凌晨1点执行一次0 0 1 1 * ?
每年的1月1日凌晨1点执行一次0 0 1 1 1 ?
每天上午10点和下午2点执行一次0 0 10,14 * * ?
每天的上午10点到11点之间每隔30秒执行一次0/30 10-11 * * * ?
每天的上午10点到11点之间每隔5分钟的1、6、11、16、21、26、31、36、41、46、51、56秒执行1,6,11,16,21,26,31,36,41,46,51,56 10-11 * * * ?

另外,如果想要快速生成 cron 表达式,可以使用在线 cron 表达式生成器,例如 cron.qqe2.com/ cron 表达式。

@EnableScheduling

使用@Scheduled需要@EnableScheduling 配合,默认 spring-boot-actuator 包会带上@EnableScheduling 注解,从而给人一种默认开启定时任务的错觉。移除对应包之后,如果用户没有在自己项目中带上 @EnableScheduling 注解,则定时任务不会生效。

Quartz

Quartz 使用 Job 和 Trigger 两个核心概念来管理任务。Job 表示要执行的任务,Trigger 表示任务的触发器,即任务执行的时间条件。Quartz 还提供了多种调度器和监听器,可以实现更加复杂的任务调度和任务管理。

以下是一个简单的 Quartz 示例:

import org.quartz.Job;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;
import org.quartz.Scheduler;
import org.quartz.SchedulerException;
import org.quartz.Trigger;
import org.quartz.TriggerBuilder;
import org.quartz.impl.StdSchedulerFactory;

public class MyJob implements Job {
    public void execute(JobExecutionContext context) throws JobExecutionException {
        System.out.println("定时任务执行了!当前时间:" + new Date());
    }

    public static void main(String[] args) throws SchedulerException {
        Scheduler scheduler = StdSchedulerFactory.getDefaultScheduler();
        JobDetail job = JobBuilder.newJob(MyJob.class).withIdentity("myJob", "group1").build();
        Trigger trigger = TriggerBuilder.newTrigger().withIdentity("myTrigger", "group1")
                .startAt(DateBuilder.todayAt(1, 0, 0)).withSchedule(SimpleScheduleBuilder.repeatHourlyForever())
                .build();
        scheduler.scheduleJob(job, trigger);
        scheduler.start();
    }
}

在上述代码中,我们定义了一个名为 MyJob 的 Job 类,并在其中实现了 execute() 方法来定义具体的任务逻辑。然后,在 main() 方法中创建了一个 Scheduler 实例,并使用 JobBuilder 和 TriggerBuilder 来定义任务和触发器。这里我们使用 SimpleScheduleBuilder 来指定任务的执行间隔,即每小时执行一次。

Quartz 还支持多种触发器类型和调度器类型,例如 CronTrigger、CalendarIntervalTrigger、JobStoreTX 等,可以根据具体的需求进行选择和配置。

参考

Spring Boot 整合 Quartz 实现 Java 定时任务的动态配置