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 小时前
图解 MongoDB 21|选举与 failover:Primary 是怎么选出来的
后端·mongodb·agent
GetcharZp3 小时前
26k Star 开源内网穿透神器 NetBird,一分钟实现全球设备互联!
后端
考虑考虑3 小时前
Mybatis实现批量插入
java·后端·mybatis
咖啡八杯4 小时前
GoF设计模式——中介者模式
java·后端·spring·设计模式
lizhongxuan6 小时前
多Agent之间的区别
后端
青石路8 小时前
记一次多JDK版本问题的排查,一坑套一坑,差点没爬上来
java
杨充8 小时前
1.面向对象设计思想
后端
IT_陈寒9 小时前
Java的Date类又坑了我一次,改用时间戳真香
前端·人工智能·后端
systemPro9 小时前
2.6亿条设备数据,历史查询从超时到50ms,我做了什么
后端
要阿尔卑斯吗9 小时前
提示词优化启示:为什么“按顺序输出“比“关键度评分“更有效
后端