Spring定时任务 Scheduled使用

文章目录

  • 前言
  • 一、@Scheduled是什么?
    • [1.1 定时任务](#1.1 定时任务)
    • [1.2 属性](#1.2 属性)
  • 二、@Scheduled使用步骤
    • [2.1 启动类增加@EnableScheduling](#2.1 启动类增加@EnableScheduling)
    • [2.2 任务执行线程池配置](#2.2 任务执行线程池配置)
    • [2.3 任务执行](#2.3 任务执行)
      • [2.3.1 cron 表达式(最常用,复杂时间规则)](#2.3.1 cron 表达式(最常用,复杂时间规则))
      • [2.3.2 fixedRate(固定频率,无视任务执行耗时)](#2.3.2 fixedRate(固定频率,无视任务执行耗时))
      • [2.3.3 fixedDelay(固定延迟,等待上一次完成)](#2.3.3 fixedDelay(固定延迟,等待上一次完成))
  • 三、注意事项:
    • [3.1 定时任务 方法要求](#3.1 定时任务 方法要求)
    • [3.2 分布式环境:](#3.2 分布式环境:)
  • 总结

前言

在项目开发中,我们通常需要使用定时任务进行在一定的时间内进行任务的触发。


一、@Scheduled是什么?

它是 Spring 框架提供的定时任务核心注解,用于标记方法作为「定时执行的任务」,让开发者无需手动编写定时器(如 Timer/ScheduledExecutorService),只需通过简单配置就能实现任务的定时 / 周期性执行。

1.1 定时任务

将一个普通的 Spring Bean 方法标记为「定时任务」,在 Spring 启动类 / 配置类上添加 @EnableScheduling 注解,开启定时任务功能,Spring 容器会根据注解中的配置,自动、周期性地调用该方法。

优势:

✅ 无需手动管理线程池(Spring 内置线程池 taskScheduler);

✅ 支持多种时间配置方式(cron 表达式、固定延迟、固定频率);

✅ 支持时区配置、任务启停控制,适配各种定时场景;

✅ 与 Spring 容器深度集成,可直接使用依赖注入(如 @Autowired)。

1.2 属性

属性名 类型 作用 示例
cron String 最灵活:基于 cron 表达式配置执行时间(支持复杂规则,如「每天 6-23 点每 30 分钟」) cron = "0 */30 6-23 * * ?"
fixedRate long 固定频率:以上一次任务开始执行的时间为基准,每隔指定毫秒执行 fixedRate = 30000(30 秒)
fixedDelay long 固定延迟:以上一次任务执行完成的时间为基准,延迟指定毫秒执行 fixedDelay = 30000(30 秒)
initialDelay long 初始延迟:项目启动后,延迟指定毫秒才执行第一次任务(配合 fixedRate/fixedDelay) initialDelay = 5000(5 秒)
zone String 时区:指定 cron 表达式的解析时区(如北京时间 Asia/Shanghai) zone = "Asia/Shanghai"
timeUnit TimeUnit 时间单位:指定 fixedRate/fixedDelay/initialDelay 的单位(默认毫秒) timeUnit = TimeUnit.SECONDS

二、@Scheduled使用步骤

2.1 启动类增加@EnableScheduling

代码如下(示例):

java 复制代码
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.scheduling.annotation.EnableScheduling;

// 核心:添加 @EnableScheduling 开启定时任务
@SpringBootApplication
@EnableScheduling
public class MyApplication {
    public static void main(String[] args) {
        SpringApplication.run(MyApplication.class, args);
    }
}

2.2 任务执行线程池配置

Spring 定时任务默认使用 单线程池,若一个任务执行耗时过长,会阻塞后续所有任务(后续任务执行没有可用的线程导致任务无法执行);

解决:自定义线程池,提高并发能力

java 复制代码
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.annotation.EnableScheduling;
import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler;

@Configuration
@EnableScheduling
public class SchedulerConfig {

    @Bean
    public ThreadPoolTaskScheduler taskScheduler() {
        ThreadPoolTaskScheduler scheduler = new ThreadPoolTaskScheduler();
        scheduler.setPoolSize(10); // 核心线程数10
        scheduler.setThreadNamePrefix("scheduled-task-"); // 线程名前缀
        scheduler.setAwaitTerminationSeconds(60); // 关闭时等待60秒
        scheduler.setWaitForTasksToCompleteOnShutdown(true); // 等待任务完成后关闭
        return scheduler;
    }
}

2.3 任务执行

2.3.1 cron 表达式(最常用,复杂时间规则)

适用于「指定时段、指定频率」的场景(如每天 6-23 点每 30 分钟执行、每周一凌晨 2 点执行)

java 复制代码
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;

// 必须是 Spring 管理的 Bean(@Component/@Service 等)
@Component
public class CronTask {

    // 北京时间 6-23 点,每30分钟执行一次(秒 分 时 日 月 周)
    @Scheduled(cron = "0 */30 6-23 * * ?", zone = "Asia/Shanghai")
    public void executeCronTask() {
        System.out.println("cron 定时任务执行:" + System.currentTimeMillis());
    }

    // 每周一凌晨 2:00 执行(? 表示不指定星期/日,避免冲突)
    @Scheduled(cron = "0 0 2 ? * MON", zone = "Asia/Shanghai")
    public void weeklyTask() {
        System.out.println("每周一凌晨2点执行:" + System.currentTimeMillis());
    }
}

2.3.2 fixedRate(固定频率,无视任务执行耗时)

适用于「不管上一次任务是否完成,到时间就执行」的场景(如每隔 30 秒执行,即使上一次任务耗时 20 秒)

执行时间线:启动 5 秒 → 第一次执行(耗时 20 秒)→ 第一次开始后 30 秒(即启动 35 秒)→ 第二次执行(无论第一次是否完成)。

java 复制代码
@Component
public class FixedRateTask {

    // 每隔30秒执行一次(初始延迟5秒,项目启动后5秒才第一次执行)
    @Scheduled(fixedRate = 30000, initialDelay = 5000)
    public void executeFixedRateTask() {
        try {
            // 模拟任务耗时20秒
            Thread.sleep(20000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("fixedRate 任务执行:" + System.currentTimeMillis());
    }
}

2.3.3 fixedDelay(固定延迟,等待上一次完成)

适用于「必须等上一次任务完成后,再延迟指定时间执行」的场景(如任务耗时不确定,避免并发执行)。

执行时间线:启动 5 秒 → 第一次执行(耗时 20 秒,启动 25 秒完成)→ 延迟 30 秒(启动 55 秒)→ 第二次执行。

java 复制代码
@Component
public class FixedDelayTask {

    // 上一次任务完成后,延迟30秒执行(时间单位显式指定为秒,更易读)
    @Scheduled(fixedDelay = 30, initialDelay = 5, timeUnit = TimeUnit.SECONDS)
    public void executeFixedDelayTask() {
        try {
            // 模拟任务耗时20秒
            Thread.sleep(20000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("fixedDelay 任务执行:" + System.currentTimeMillis());
    }
}

三、注意事项:

3.1 定时任务 方法要求

复制代码
必须是 无返回值(void) 方法(返回值会被忽略);
必须是 无参数 方法(若需参数,可通过依赖注入获取,而非方法参数);
必须是 Spring Bean(需加 @Component/@Service 等注解,否则 Spring 无法扫描到)

3.2 分布式环境:

单机 @Scheduled 不支持分布式(多实例部署时,多个实例会同时执行任务);

解决:使用分布式定时任务框架(如 XXL-Job、Quartz 分布式版、Sentinel 限流),或基于 Redis 分布式锁实现任

务互斥。


总结

@Scheduled 是 Spring 实现本地定时任务的核心注解,标记方法为定时任务,支持 cron、固定频率、固定延迟三种时间规则;启动类加 @EnableScheduling,方法需是无参、void、Spring Bean;cron 适合复杂时间规则,zone 解决时区问题,自定义线程池避免任务阻塞;默认单线程、不支持分布式,需针对性优化。

相关推荐
贾修行2 小时前
IIS 作为反向代理:为 ASP.NET Core Kestrel 应用保驾护航
后端·iis·asp.net·反向代理·arr·url 重写规则
沉默-_-2 小时前
力扣hot100双指针专题解析2(C++)
java·c++·算法·蓝桥杯·双指针
Jaxson Lin2 小时前
Java编程进阶:智能仿真无人机项目1.0
java·开发语言
sheji34162 小时前
【开题答辩全过程】以 实验室设备管理系统为例,包含答辩的问题和答案
java
江君是实在人2 小时前
java 面试题 redis 处理大key问题
java·开发语言·redis
Filotimo_2 小时前
Spring MVC 注解家族
java
禁默2 小时前
Portainer:让 Docker 管理变简单,用cpolar突破局域网后协作更轻松
java·docker·容器·cpolar
麦兜*3 小时前
SpringBoot 3.x新特性全面解析:从Java 17支持到GraalVM原生镜像
java·spring boot·后端
你知道“铁甲小宝”吗丶3 小时前
【第2章】第一个Go程序
后端·go