5分钟搞懂如何在Spring Boot中Schedule Tasks

本文内容已经同步更新到 Github:github.com/Snailclimb/… ,欢迎 star。java

不少时候咱们都须要为系统创建一个定时任务来帮咱们作一些事情,SpringBoot 已经帮咱们实现好了一个,咱们只须要直接使用便可,固然你也能够不用 SpringBoot 自带的定时任务,整合 Quartz 不少时候也是一个不错的选择。git

本文不涉及 SpringBoot 整合 Quartz 的内容,只演示了如何使用 SpringBoot 自带的实现定时任务的方式。github

Spring Schedule 实现定时任务

咱们只须要 SpringBoot 项目最基本的依赖便可,因此这里就不贴配置文件了。面试

1. 建立一个 scheduled task

咱们使用 @Scheduled 注解就能很方便地建立一个定时任务,下面的代码中涵盖了 @Scheduled的常见用法,包括:固定速率执行、固定延迟执行、初始延迟执行、使用 Cron 表达式执行定时任务。spring

Cron 表达式: 主要用于定时做业(定时任务)系统定义执行时间或执行频率的表达式,很是厉害,你能够经过 Cron 表达式进行设置定时任务天天或者每月何时执行等等操做。springboot

推荐一个在线Cron表达式生成器:cron.qqe2.com/bash

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;

import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.concurrent.TimeUnit;

/** * @author shuang.kou */
@Component
public class ScheduledTasks {
    private static final Logger log = LoggerFactory.getLogger(ScheduledTasks.class);
    private static final SimpleDateFormat dateFormat = new SimpleDateFormat("HH:mm:ss");

    /** * fixedRate:固定速率执行。每5秒执行一次。 */
    @Scheduled(fixedRate = 5000)
    public void reportCurrentTimeWithFixedRate() {
        log.info("Current Thread : {}", Thread.currentThread().getName());
        log.info("Fixed Rate Task : The time is now {}", dateFormat.format(new Date()));
    }

    /** * fixedDelay:固定延迟执行。距离上一次调用成功后2秒才执。 */
    @Scheduled(fixedDelay = 2000)
    public void reportCurrentTimeWithFixedDelay() {
        try {
            TimeUnit.SECONDS.sleep(3);
            log.info("Fixed Delay Task : The time is now {}", dateFormat.format(new Date()));
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    /** * initialDelay:初始延迟。任务的第一次执行将延迟5秒,而后将以5秒的固定间隔执行。 */
    @Scheduled(initialDelay = 5000, fixedRate = 5000)
    public void reportCurrentTimeWithInitialDelay() {
        log.info("Fixed Rate Task with Initial Delay : The time is now {}", dateFormat.format(new Date()));
    }

    /** * cron:使用Cron表达式。 每分钟的1,2秒运行 */
    @Scheduled(cron = "1-2 * * * * ? ")
    public void reportCurrentTimeWithCronExpression() {
        log.info("Cron Expression: The time is now {}", dateFormat.format(new Date()));
    }
}

复制代码

关于 fixedRate 这里其实有个坑,假如咱们有这样一种状况:咱们某个方法的定时器设定的固定速率是每5秒执行一次。这个方法如今要执行下面四个任务,四个任务的耗时是:6 s、6s、 2s、 3s,请问这些任务默认状况下(单线程)将如何被执行?ide

咱们写一段简单的程序验证:学习

private static final Logger log = LoggerFactory.getLogger(AsyncScheduledTasks.class);
    private static final SimpleDateFormat dateFormat = new SimpleDateFormat("HH:mm:ss");
    private List<Integer> index = Arrays.asList(6, 6, 2, 3);
    int i = 0;
    @Scheduled(fixedRate = 5000)
    public void reportCurrentTimeWithFixedRate() {
        if (i == 0) {
            log.info("Start time is {}", dateFormat.format(new Date()));
        }
        if (i < 5) {
            try {
                TimeUnit.SECONDS.sleep(index.get(i));
                log.info("Fixed Rate Task : The time is now {}", dateFormat.format(new Date()));
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            i++;
        }
    }
复制代码

运行程序输出以下:ui

Start time is 20:58:33
Fixed Rate Task : The time is now 20:58:39
Fixed Rate Task : The time is now 20:58:45
Fixed Rate Task : The time is now 20:58:47
Fixed Rate Task : The time is now 20:58:51
复制代码

看下面的运行任务示意图应该很好理解了。

若是咱们将这个方法改成并行运行,运行结果就大相径庭了。

2. 启动类上加上@EnableScheduling注解

在 SpringBoot 中咱们只须要在启动类上加上@EnableScheduling即可以启动定时任务了。

@SpringBootApplication
@EnableScheduling
public class DemoApplication {

    public static void main(String[] args) {
        SpringApplication.run(DemoApplication.class, args);
    }
}
复制代码

3. 自定义线程池执行 scheduled task

默认状况下,@Scheduled任务都在Spring建立的大小为1的默认线程池中执行,你能够经过在加了@Scheduled注解的方法里加上下面这段代码来验证。

logger.info("Current Thread : {}", Thread.currentThread().getName());
复制代码

你会发现加上上面这段代码的定时任务,每次运行都会输出:

Current Thread : scheduling-1
复制代码

若是咱们须要自定义线程池执行话只须要新加一个实现SchedulingConfigurer接口的 configureTasks 的类便可,这个类须要加上 @Configuration 注解。

@Configuration
public class SchedulerConfig implements SchedulingConfigurer {
    private final int POOL_SIZE = 10;

    @Override
    public void configureTasks(ScheduledTaskRegistrar scheduledTaskRegistrar) {
        ThreadPoolTaskScheduler threadPoolTaskScheduler = new ThreadPoolTaskScheduler();

        threadPoolTaskScheduler.setPoolSize(POOL_SIZE);
        threadPoolTaskScheduler.setThreadNamePrefix("my-scheduled-task-pool-");
        threadPoolTaskScheduler.initialize();

        scheduledTaskRegistrar.setTaskScheduler(threadPoolTaskScheduler);
    }
}
复制代码

经过上面的验证的方式输出当前线程的名字会改变。

4. @EnableAsync 和 @Async 使定时任务并行执行

若是你想要你的代码并行执行的话,还能够经过@EnableAsync 和 @Async这两个注解实现

@Component
@EnableAsync
public class AsyncScheduledTasks {
    private static final Logger log = LoggerFactory.getLogger(AsyncScheduledTasks.class);
    private static final SimpleDateFormat dateFormat = new SimpleDateFormat("HH:mm:ss");

    /** * fixedDelay:固定延迟执行。距离上一次调用成功后2秒才执。 */
    //@Async
    @Scheduled(fixedDelay = 2000)
    public void reportCurrentTimeWithFixedDelay() {
        try {
            TimeUnit.SECONDS.sleep(3);
            log.info("Fixed Delay Task : The time is now {}", dateFormat.format(new Date()));
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}
复制代码

运行程序输出以下,reportCurrentTimeWithFixedDelay() 方法会每5秒执行一次,由于咱们说过了@Scheduled任务都在Spring建立的大小为1的默认线程池中执行。

Current Thread : scheduling-1
Fixed Delay Task : The time is now 14:24:23
Current Thread : scheduling-1
Fixed Delay Task : The time is now 14:24:28
Current Thread : scheduling-1
Fixed Delay Task : The time is now 14:24:33
复制代码

reportCurrentTimeWithFixedDelay() 方法上加上 @Async 注解后输出以下,reportCurrentTimeWithFixedDelay() 方法会每 2 秒执行一次。

Current Thread : task-1
Fixed Delay Task : The time is now 14:27:32
Current Thread : task-2
Fixed Delay Task : The time is now 14:27:34
Current Thread : task-3
Fixed Delay Task : The time is now 14:27:36
复制代码

公众号

若是你们想要实时关注我更新的文章以及分享的干货的话,能够关注个人公众号。

《Java面试突击》: 由本文档衍生的专为面试而生的《Java面试突击》V2.0 PDF 版本公众号后台回复 "Java面试突击" 便可免费领取!

Java工程师必备学习资源: 一些Java工程师经常使用学习资源公众号后台回复关键字 “1” 便可免费无套路获取。

个人公众号
相关文章
相关标签/搜索