Java【付诸实践 03】Spring定时任务注解@Scheduled+@EnableAsync用法详解(简单说明+应用场景+demo源代码+执行过程分析)

简介: Java【付诸实践 03】Spring定时任务注解@Scheduled+@EnableAsync用法详解(简单说明+应用场景+demo源代码+执行过程分析)

@Scheduled 由Spring定义,用于将方法设置为调度任务,可实现方法的周期或定时执行。想单独使用Scheduling,需引入spring-context这个依赖。spring-boot-starter-web已经集成了spring-context,所以可以直接使用Scheduling模块(我使用的 SpringBoo t版本为 2.2.4.RELEASE)。

1.Demo走起

话不多说,先写一个小Demo让定时任务跑起来,然后再一步步添枝加叶层层深入。两个注解【@EnableScheduling + @Scheduled】实现起步:

// 第一个注解@EnableScheduling
@EnableScheduling
@SpringBootApplication
public class SBApplication implements ApplicationRunner {
    // ApplicationRunner 和 CommandLineRunner 可用于数据配置和预处理 参数类型不一样
    public static void main(String[] args) {
        SpringApplication.run(SBApplication.class, args);
    }
    @Bean
    public CommandLineRunner commandLineRunner(ApplicationContext ctx) {
        return args -> {
            System.out.println("Let's inspect the beans provided by Spring Boot:");
            String[] beanNames = ctx.getBeanDefinitionNames();
            Arrays.sort(beanNames);
            /*
             * for (String name : beanNames) { System.out.println(name); }
             */
        };
    }
    @Override
    public void run(ApplicationArguments args) throws Exception {
        /* 可以在springBoot启动时执行预处理程序 */
        System.out.println("启动时就打印的数据!");
    }
}
// 第二个注解 @Scheduled
@Component
public class Scheduler {
    @Scheduled(fixedRate = 1000, initialDelay = 3000)
    public void fixedDelay() {
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS");
        String dateString = sdf.format(new Date());
        System.out.println(dateString);
    }
}

Demo实现的是项目启动3s后每秒输出当前时间,日志是诚实的…

2021-06-24 15:18:06.798  INFO 16108 --- [)-192.168.0.108] o.s.web.servlet.DispatcherServlet        : Completed initialization in 5 ms
2021-06-24 15:18:09.559
2021-06-24 15:18:10.559
2021-06-24 15:18:11.558
2021-06-24 15:18:12.558
2021-06-24 15:18:13.558
2021-06-24 15:18:14.558
2021-06-24 15:18:15.558

2.@Scheduled用法

// 先看看源码 spring版本 5.2.3.RELEASE
@Target({ElementType.METHOD, ElementType.ANNOTATION_TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Repeatable(Schedules.class)
public @interface Scheduled {
    String CRON_DISABLED = "-";
    String cron() default "";
    String zone() default "";
    long fixedDelay() default -1L;
    String fixedDelayString() default "";
    long fixedRate() default -1L;
    String fixedRateString() default "";
    long initialDelay() default -1L;
    String initialDelayString() default "";
}

注解说明:

  • @Target({ElementType.METHOD, ElementType.ANNOTATION_TYPE}):表示@Schduled可以被使用在方法和注解上。
  • @Retention(RetentionPolicy.RUNTIME):表示@Schduled的生命周期是在程序运行时。
  • @Repeatable(Schedules.class):表示@Schduled可以被在同一个地方重复使用多次,参数存放在Schedules类中(我们可以点进去看Schedules类,发现里面有一个Scheduled数组对象,用来保存多个Scheduled的配置)。

属性说明:

  • String CRON_DISABLED:定时禁用标志。
  • String cron():定义定时执行时间,默认值为空字符串,表示该值无效。
  • String zone():定义时区,默认值为空字符串。
  • long fixedDelay():定义任务下次开始执行的间隔时间,从上一次任务执行完成开始计算,单位毫秒。默认值-1L,表示该值设置无效。
  • String fixedDelayString():定义任务下次开始执行的间隔时间,从上一次任务执行完成开始计算,单位毫秒。与fixedDelay不同只在于值的格式。默认值"",表示该值设置无效。
  • long fixedRate():定义每两次任务的间隔频率,从上一次任务开始执行开始计算,单位毫秒。默认值-1L,表示该值无效。
  • String fixedRateString():定义每两次任务的间隔频率,从上一次任务开始执行开始计算,单位毫秒。与fixedRate不同只在于值的格式。默认值为空字符串,表示该值无效。
  • long initialDelay():定义第一次执行的延迟执行时间,单位秒。默认值-1L,表示没有延迟。
  • String initialDelayString():定义第一次执行的延迟执行时间,与initialDelayDelay()不同在于这里使用表达式,而不是以秒为单位。默认值为空字符串,表示没有延迟。

特别注意:

我们要区分fixedDelay和fixedRate的不同,前者是当前方法执行完毕后加上间隔时间再去执行方法,后者不管任务是否执行完毕间隔时间一到就会再次执行方法。

3.cron

前面的demo实现的是简单的延时和频率执行。使用cron可以实现定时定频执行。这里的cron跟Linux上的cron是不同的,Linux上的表达式没有秒,大家要注意。

cron表达式是一个字符串,字符串以5或6个空格隔开,分为6或7个域,每一个域代表一个含义,由于年份非必填所以cron有如下两种语法格式:

  1. Seconds Minutes Hours DayofMonth Month DayofWeek Year
  2. Seconds Minutes Hours DayofMonth Month DayofWeek

每一个域可出现的字符如下:

含义:

  • *:表示匹配该域的任意值,假如在Minutes域使用*, 即表示每分钟都会触发事件。
  • ?:只能用在DayofMonth和DayofWeek两个域。它也匹配域的任意值,但实际不会。因为DayofMonth和 DayofWeek会相互影响。例如想在每月的20日触发调度,不管20日到底是星期几,则只能使用如下写法: 13 13 15 20 * ?, 其中最后一位只能用?,而不能使用*,如果使用*表示不管星期几都会触发,实际上并不是这样。
  • -:表示范围,例如在Minutes域使用5-20,表示从5分到20分钟每分钟触发一次。
  • /:表示起始时间开始触发,然后每隔固定时间触发一次,例如在Minutes域使用5/20,则意味着5分钟触发一次,而25,45等分别触发一次。
  • ,:表示列出枚举值值。例如:在Minutes域使用5,20,则意味着在5和20分每分钟触发一次。
  • L:表示最后,只能出现在DayofWeek和DayofMonth域,如果在DayofWeek域使用5L,意味着在最后的一个星期四触发。
  • W:表示有效工作日(周一到周五),只能出现在DayofMonth域,系统将在离指定日期的最近的有效工作日触发事件。例如:在 DayofMonth使用5W,如果5日是星期六,则将在最近的工作日:星期五,即4日触发。如果5日是星期天,则在6日(周一)触发;如果5日在星期一 到星期五中的一天,则就在5日触发。另外一点,W的最近寻找不会跨过月份。
  • LW:这两个字符可以连用,表示在某个月最后一个工作日,即最后一个星期五。
  • #:用于确定每个月第几个星期几,只能出现在DayofMonth域。例如在4#2,表示某月的第二个星期三。

举几个例子【这里是举一反三的环节】 😃

  • 每隔5秒执行一次:*/5 * * * * ?
  • 每隔1分钟执行一次:0 */1 * * * ? 【如果秒用 * 这样就是每分每秒都执行】
  • 每天23点执行一次:0 0 23 * * ? 【如果前面的两个0 用了 * 那就是23点的每分每秒都执行】
  • 每月最后一天23点执行一次:0 0 23 L * ?
  • 每周星期天凌晨1点实行一次:0 0 1 ? * L
  • 每天的0点、13点、18点、21点都执行一次:0 0 0,13,18,21 * * ?
  • 表示在每月的1日的凌晨2点调度任务:0 0 2 1 * ? *
  • 表示周一到周五每天上午10:15执行作业:0 15 10 ? * MON-FRI
  • 表示2002-2006年的每个月的最后一个星期五上午10:15执行:0 15 10 ? 6L 2002-2006
    注意:由于"月份中的日期"和"星期中的日期"这两个元素互斥的,必须要对其中一个设置?
目录
相关文章
|
18天前
|
Java
Java中的抽象类:深入了解抽象类的概念和用法
Java中的抽象类是一种不能实例化的特殊类,常作为其他类的父类模板,定义子类行为和属性。抽象类包含抽象方法(无实现)和非抽象方法。定义抽象类用`abstract`关键字,子类继承并实现抽象方法。抽象类适用于定义通用模板、复用代码和强制子类实现特定方法。优点是提供抽象模板和代码复用,缺点是限制继承灵活性和增加类复杂性。与接口相比,抽象类可包含成员变量和单继承。使用时注意设计合理的抽象类结构,谨慎使用抽象方法,并遵循命名规范。抽象类是提高代码质量的重要工具。
33 1
|
16天前
|
Java 调度
Java中常见锁的分类及概念分析
Java中常见锁的分类及概念分析
16 0
|
16天前
|
Java
Java中ReentrantLock中tryLock()方法加锁分析
Java中ReentrantLock中tryLock()方法加锁分析
13 0
|
18天前
|
Java 数据挖掘
java实践
【4月更文挑战第9天】java实践
12 1
|
22小时前
|
Java 关系型数据库 MySQL
万字长文带你详聊Java注解本质
万字长文带你详聊Java注解本质
7 0
|
2天前
|
数据采集 存储 Java
高德地图爬虫实践:Java多线程并发处理策略
高德地图爬虫实践:Java多线程并发处理策略
|
3天前
|
IDE Java 开发工具
Java从入门到精通:1.3.1实践编程巩固基础知识
Java从入门到精通:1.3.1实践编程巩固基础知识
|
4天前
|
Java 测试技术 编译器
JAVA注解
JAVA注解
9 0
|
8天前
|
JSON Java 数据格式
Java QueryWrapper基本用法
Java QueryWrapper基本用法
13 2
|
9天前
|
安全 Java 程序员
Java中的多线程并发编程实践
【4月更文挑战第18天】在现代软件开发中,为了提高程序性能和响应速度,经常需要利用多线程技术来实现并发执行。本文将深入探讨Java语言中的多线程机制,包括线程的创建、启动、同步以及线程池的使用等关键技术点。我们将通过具体代码实例,分析多线程编程的优势与挑战,并提出一系列优化策略来确保多线程环境下的程序稳定性和性能。