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天前
|
XML Java 编译器
Java注解的底层源码剖析与技术认识
Java注解(Annotation)是Java 5引入的一种新特性,它提供了一种在代码中添加元数据(Metadata)的方式。注解本身并不是代码的一部分,它们不会直接影响代码的执行,但可以在编译、类加载和运行时被读取和处理。注解为开发者提供了一种以非侵入性的方式为代码提供额外信息的手段,这些信息可以用于生成文档、编译时检查、运行时处理等。
54 7
|
23天前
|
Java 编译器 数据库
Java 中的注解(Annotations):代码中的 “元数据” 魔法
Java注解是代码中的“元数据”标签,不直接参与业务逻辑,但在编译或运行时提供重要信息。本文介绍了注解的基础语法、内置注解的应用场景,以及如何自定义注解和结合AOP技术实现方法执行日志记录,展示了注解在提升代码质量、简化开发流程和增强程序功能方面的强大作用。
63 5
|
24天前
|
监控 算法 Java
jvm-48-java 变更导致压测应用性能下降,如何分析定位原因?
【11月更文挑战第17天】当JVM相关变更导致压测应用性能下降时,可通过检查变更内容(如JVM参数、Java版本、代码变更)、收集性能监控数据(使用JVM监控工具、应用性能监控工具、系统资源监控)、分析垃圾回收情况(GC日志分析、内存泄漏检查)、分析线程和锁(线程状态分析、锁竞争分析)及分析代码执行路径(使用代码性能分析工具、代码审查)等步骤来定位和解决问题。
|
1月前
|
Java 开发者 Spring
[Java]自定义注解
本文介绍了Java中的四个元注解(@Target、@Retention、@Documented、@Inherited)及其使用方法,并详细讲解了自定义注解的定义和使用细节。文章还提到了Spring框架中的@AliasFor注解,通过示例帮助读者更好地理解和应用这些注解。文中强调了注解的生命周期、继承性和文档化特性,适合初学者和进阶开发者参考。
59 14
|
1月前
|
存储 Java 关系型数据库
在Java开发中,数据库连接是应用与数据交互的关键环节。本文通过案例分析,深入探讨Java连接池的原理与最佳实践
在Java开发中,数据库连接是应用与数据交互的关键环节。本文通过案例分析,深入探讨Java连接池的原理与最佳实践,包括连接创建、分配、复用和释放等操作,并通过电商应用实例展示了如何选择合适的连接池库(如HikariCP)和配置参数,实现高效、稳定的数据库连接管理。
66 2
|
1月前
|
Java 关系型数据库 数据库
面向对象设计原则在Java中的实现与案例分析
【10月更文挑战第25天】本文通过Java语言的具体实现和案例分析,详细介绍了面向对象设计的五大核心原则:单一职责原则、开闭原则、里氏替换原则、接口隔离原则和依赖倒置原则。这些原则帮助开发者构建更加灵活、可维护和可扩展的系统,不仅适用于Java,也适用于其他面向对象编程语言。
37 2
|
1月前
|
Java 编译器
Java进阶之标准注解
Java进阶之标准注解
34 0
|
1天前
|
安全 Java 编译器
深入理解Java中synchronized三种使用方式:助您写出线程安全的代码
`synchronized` 是 Java 中的关键字,用于实现线程同步,确保多个线程互斥访问共享资源。它通过内置的监视器锁机制,防止多个线程同时执行被 `synchronized` 修饰的方法或代码块。`synchronized` 可以修饰非静态方法、静态方法和代码块,分别锁定实例对象、类对象或指定的对象。其底层原理基于 JVM 的指令和对象的监视器,JDK 1.6 后引入了偏向锁、轻量级锁等优化措施,提高了性能。
11 3
|
1天前
|
存储 安全 Java
Java多线程编程秘籍:各种方案一网打尽,不要错过!
Java 中实现多线程的方式主要有四种:继承 Thread 类、实现 Runnable 接口、实现 Callable 接口和使用线程池。每种方式各有优缺点,适用于不同的场景。继承 Thread 类最简单,实现 Runnable 接口更灵活,Callable 接口支持返回结果,线程池则便于管理和复用线程。实际应用中可根据需求选择合适的方式。此外,还介绍了多线程相关的常见面试问题及答案,涵盖线程概念、线程安全、线程池等知识点。
10 2
|
1天前
|
消息中间件 缓存 安全
Java多线程是什么
Java多线程简介:本文介绍了Java中常见的线程池类型,包括`newCachedThreadPool`(适用于短期异步任务)、`newFixedThreadPool`(适用于固定数量的长期任务)、`newScheduledThreadPool`(支持定时和周期性任务)以及`newSingleThreadExecutor`(保证任务顺序执行)。同时,文章还讲解了Java中的锁机制,如`synchronized`关键字、CAS操作及其实现方式,并详细描述了可重入锁`ReentrantLock`和读写锁`ReadWriteLock`的工作原理与应用场景。