首页 > 编程笔记 > Java笔记 阅读:31

SpringBoot @Scheduled定时任务详解(附带实例)

在日常的项目开发中,往往会需要后台定时执行某项任务,比如自动将超过 24 小时的未付款订单改为取消状态,自动将超过 14 天客户未签收的订单改为已签收状态等。

如何在 Spring Boot 中实现后台定时任务的功能呢?本节将介绍使用 Spring Boot 内置的 @Scheduled 注解来实现定时任务。

@Scheduled注解实现定时任务

Spring Boot 提供了内置的 @Scheduled 注解实现定时任务的功能。使用 @Scheduled 注解创建定时任务非常简单,只需几行代码即可完成。

下面演示使用 @Scheduled 注解实现定时任务:

1) 修改启动类

在启动类上加上 @EnableScheduling 开启定时任务,具体的代码如下:
@SpringBootApplication
@EnableScheduling
public class Application {
    public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
    }
}
在上面的示例中,使用 @EnableScheduling 注解打开定时功能之后,默认情况下,系统会自动启动一个线程,调度执行定义的后台定时任务。

2) 创建定时任务类

首先创建 SchedulerTask 类,然后在任务方法上添加 @Scheduled 注解,具体的代码如下:
@Component
public class SchedulerTask {
    private static final Logger logger = LoggerFactory.getLogger(SchedulerTask.class);

    @Scheduled(cron="*/10 * * * * ?")
    private void taskCron(){
        SimpleDateFormat dateFormat = new SimpleDateFormat("HH:mm:ss");
        logger.info("现在时间Scheduled1: " + dateFormat.format(new Date()));
    }
}
在上面的示例中,创建了 taskCron 的定时任务,然后在 taskCron() 方法中增加了 @Scheduled 注解设置 Cron 表达式,设置任务每隔 10 秒执行一次。

@Scheduled 不仅支持以 Cron 表达式的方式定义执行周期,还支持以固定时间间隔的方式调度任务。下面定义一个固定时间间隔执行的任务,具体的代码如下:
@Scheduled(fixedRate = 10000)
public void taskFixed() {
    SimpleDateFormat dateFormat = new SimpleDateFormat("HH:mm:ss");
    logger.info("现在时间Scheduled2: " + dateFormat.format(new Date()));
}
在上面的示例中,使用 fixedRate 参数就是指固定的时间间隔,单位是毫秒,即设置任务每隔 10 秒执行一次。

3) 启动项目

创建好 SchedulerTask 定时任务后启动项目,查看后台任务的运行情况,如下图所示:


图 1 后台定时任务执行日志

后台日志显示,SchedulerTask 任务每隔 10 秒输出当前时间,说明定义的任务正在后台定时执行。

@Scheduled时间参数设置

@Scheduled 注解可以接受两种定时的参数设置:
同时,@Scheduled 还支持简单的延时操作,比如 fixedDelay、initialDelay 后面填写相应的毫秒数即可:
总的来说,fixedRate 简单易懂,而 Cron 表达式功能灵活,支持定义复杂的时间规则。二者各有优劣,使用时可根据实际的业务需求选择。

多线程定时任务

默认情况下,Spring Boot 定时任务是按单线程方式执行的,也就是说,如果同一时刻有两个定时任务需要执行,那么只能在一个定时任务完成之后再执行下一个。

如果只有一个定时任务,这样做肯定没问题;当定时任务增多时,如果一个任务被阻塞,则会导致其他任务无法正常执行。要解决这个问题,需要配置任务调度线程池。

1) 增加多线程配置类

在 config 目录下增加 SchedulerConfig 配置类,代码如下:
public class SchedulerConfig {
    @Bean
    public Executor taskScheduler() {
        ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
        executor.setCorePoolSize(3);
        executor.setMaxPoolSize(10);
        executor.setQueueCapacity(3);
        executor.initialize();
        return executor;
    }
}
设置执行线程池为 3,最大线程数为 10。

2) 修改SchedulerTask定时任务

修改之前定义的 SchedulerTask 定时任务的类,在方法上增加 @Async 注解,使得后台任务能够异步执行,代码如下:
@EnableAsync // 开启异步事件的支持
@Component
public class SchedulerTask {
    private static final Logger logger = LoggerFactory.getLogger(SchedulerTask.class);
    @Async
    @Scheduled(cron="*/10 * * * * ?")
    public void taskCron() {
        SimpleDateFormat dateFormat = new SimpleDateFormat("HH:mm:ss");
        logger.info("SchedulerTask taskCron 现在时间: " + dateFormat.format(new Date()));
    }

    @Async
    @Scheduled(fixedRate = 5000)
    public void taskFixed() {
        SimpleDateFormat dateFormat = new SimpleDateFormat("HH:mm:ss");
        logger.info("SchedulerTask taskFixed 现在时间: " + dateFormat.format(new Date()));
    }
}
在上面的示例中,定时任务类 SechedulerTask 增加了 @EnableAsync 注解,开启了异步事件支持。同时,在定时方法上增加 @Async 注解,使任务能够异步执行,这样各个后台任务就不会阻塞。

3) 启动项目

配置修改完成后,重新启动项目,查看后台任务的运行情况。如下图所示,全部的后台任务分成了多个线程执行,这样任务之间不会相互影响。


图 2 后台定时任务执行日志

通过后台日志可以看到,Spring Boot 启动线程池负责调度执行后台任务,各个后台任务之间相对独立、互不影响。

相关文章