定时任务基本使用指南(cron 时间表达式、Spring 自带调度器、JDK 原生定时器)

简介: 定时任务基本使用指南(cron 时间表达式、Spring 自带调度器、JDK 原生定时器)

cron 时间表达式(七子表达式)

cron 表达式,又称时间表达式(七子表达式),是一个字符串,以5或者6个空格隔开,字符串被切割为6个或者7个域,每个域都代表不同的含义

// 从左到右分别表示:秒 分 时 日 月 周 年;参数以空格隔开,其中 年 不是必须参数,可以省略。
{Seconds} {Minutes} {Hours} {DayofMonth} {Month} {DayofWeek} {Year}
序号 时间元素 是否必填 入参范围 可填通配符
1 0-59 , - * /
2 0-59 , - * /
3 0-23 , - * /
4 1-31 , - * ? / L W
5 1-12 或 JAN - DEC , - * /
6 周(周一 ~ 周日) 1-7(1=SUN )<br/>或 SUN,MON,TUE,WED,THU,FRI,SAT , - * ? / L #
8 1970-2099 , - * /

常用通配符:

*    表示匹配该域的任意值,比如 Minutes 域使用 * 表示每分钟都会触发
-    表示范围,比如 Minutes 域使用 10-20 表示从10分钟到20分钟每分钟都会触发一次
,    表示列出枚举值,比如 Minutes 域使用 1,3 表示1分钟和3分钟都会触发一次
/    表示间隔时间触发(开始时间/时间间隔),例如在 Minutes 域使用 5/10 表示从第5分钟开始,每隔10分钟触发一次
?    表示不指定值,简单理解就是忽略该字段的值,直接根据另一个字段的值触发执行,仅被用于 DayofMonth 域和 DayofWeek 域中
    当这两个域其中之一被指定了值以后,为了避免冲突,需要将另一个域的值设为“?”
L    表示最后,是单词"last"的缩写(最后一天或最后一个星期几);仅被用于 DayofMonth 域和 DayofWeek 域中
        用在 DayofMonth 域 表示该月的最后一天
        用在 DayofWeek 域 表示一个星期的最后一天,也就是周六
    在使用'L'时,不要指定列表','或范围'-',否则易导致出现意料之外的结果
    如果在“L”前有具体的内容,它就具有其他的含义了
        例如:“6L”表示这个月的倒数第6天,“FRIL”表示这个月的最一个星期五
    注意:在使用“L”参数时,不要指定列表或范围,因为这会导致问题
#    表示该月第n个星期x(x#n),仅用 DayofWeek 域,如:6#3 表示该月的第三个星期五
W    仅用在 DayofMonth 域中,表示距离当月给定日期最近的工作日(周一到周五),是单词"weekday"的缩写

注意:天数和星期不能同时指定值!!!

示例:

// 每天凌晨零点执行
@Scheduled(cron ="0 0 0 * * * ?")
// 每隔五分钟执行
@Scheduled(cron ="0 */5 * * * * ?")
// 在每天下午2点到下午2:55期间的每5分钟触发
@Scheduled(cron ="0 0/5 14 * * ?")
// 在每天下午2点到2:55期间和下午6点到6:55期间的每5分钟触发
@Scheduled(cron ="0 0/5 14,18 * * ?")
// 在每天下午2点到下午2:05期间的每1分钟触发
@Scheduled(cron ="0 0-5 14 * * ?")
// 每个星期三中午12点
@Scheduled(cron ="0 0 12 ? * WED")
// 每月的第三个星期五上午10:15触发
@Scheduled(cron ="0 15 10 ? * 6#3")
// 每年三月的星期三的下午2:10和2:44触发
@Scheduled(cron ="0 10,44 14 ? 3 WED")
// 每月最后一日的上午10:15触发
@Scheduled(cron ="0 15 10 L * ?")
// 每月的最后一个星期五上午10:15触发
@Scheduled(cron ="0 15 10 ? * 6L")

<br/>

spring 自带调度器:@Scheduled

介绍

定时调度其实有很多的第三方平台可以接入,但是其实在 SpringBoot 有自带的定时任务注解 @Scheduled。@Scheduled 可以通过注解配置快速实现方法的定时调度,直接在方法加上 @Scheduled 注解即可。

定时任务开启条件:

  • @Scheduled 标注的方法所在类交给 Spring 容器管理(即标注 Spring IOC 注解)
  • 在配置类或 @Scheduled 标注的方法所在类上添加 @EnableScheduling 注解开启定时任务功能

    @EnableScheduling 注解的作用是发现注解@Scheduled的任务并后台执行

<br/>

@Scheduled注解参数:

  • cron 参数:数据类型为 String

    最经常使用的参数,表示接收一个时间表达式(七子表达式),最多接收7个时间元素

  • zone 参数

    指定获取的时区,默认是空,表示使用服务器所在时区,比如 Asia/BeiJingi 或者 Asia/Shanghai

  • fixedDelay 参数

    指定上次执行结束后到再次执行的固定时间,单位是毫秒。

    示例:@Scheduled(fixedDelay= 3000) 表示距离上次调用后三秒再执行

  • fixedDelayString 参数

    fixedDelayString 与 fixedDelay 是几乎一样的,唯一的差异是 fixedDelayString 是支持占位符的

  • fixedRate 参数

    指定多久执行一次,单位是毫秒。与 cron 参数的 / 通配符效果相似

    示例:@Scheduled(fixedRate= 3000) 表示每三秒执行一次

  • fixedRateString 参数

    fixedRate 参数的升级,支持占位符

  • initialDelay 参数

    表示第一次延迟多少毫秒执行,单位是毫秒

    示例:@Scheduled(initialDelay= 3000) 表示第一次执行时,延迟3秒执行

  • initialDelayString 参数

    initialDelay 参数的升级,支持占位符

    <br/>

注意

若微服务是多实例部署,则每一个微服务实例都会同时执行定时调度任务,可能产生很多重复数据或者导致系统出现其他的业务逻辑BUG,所以在使用 @Scheduled 进行任务调度时,需要配合 redis 的分布式锁来使用,让定时调度任务只在一个微服务实例上执行,避免BUG出现。

<br/>

使用示例

import lombok.extern.slf4j.Slf4j;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;
 
import java.time.LocalDateTime;
 
@Component
@Slf4j
public class Task {
    
    // 每5秒执行一次
    @Scheduled(cron = "0/5 * * * * ?")
    public void task2(){
        log.warn("现在时间:" + LocalDateTime.now());
    }
    
    // 上⼀次开始执⾏时间点之后2秒再执⾏
    @Scheduled(fixedRate = 2000)
    public void task(){
        log.warn("现在时间:" + LocalDateTime.now());
    }
}

<br/>

JDK 原生定时器

介绍

定时器,可以设置线程在某个时间执行某件事情,或者某个时间开始,每间隔指定的时间反复的做某件事情

java.util.Timer 类:是一个描述定时器的类

一种工具,线程用其安排以后在后台线程中执行的任务。可安排任务执行一次,或者定期重复执行。

构造方法:

public Timer()        // 创建一个新计时器。

成员方法:

// 终止此计时器,丢弃所有当前已安排的任务
void cancel()
    // 注意,在此计时器调用的计时器任务的 run 方法内调用此方法,就可以绝对确保正在执行的任务是此计时器所执行的最后一个任务。
    
// 在指定的毫秒值之后,执行指定的任务,只会执行一次
void schedule(TimerTask task, long delay)     
// 在指定的毫秒值之后,执行指定的任务,之后每隔固定的毫秒数重复执行定时任务
void schedule(TimerTask task, long delay, long period)
// 安排在指定的时间执行指定的任务,只会执行一次
void schedule(TimerTask task, Date time) 
// 安排指定的任务在指定的时间开始进行重复的固定延迟执行
void schedule(TimerTask task, Date firstTime, long period) 
/* 参数:
        task        所要安排的任务。定时器到时间之后要执行的任务
        delay        执行任务前的延迟时间,单位是毫秒。 多个毫秒之后开始执行TimerTask任务
        period        执行各后续任务之间的时间间隔,单位是毫秒。定时器开始执行之后,每隔多少毫秒重复执行
        time        执行任务的时间。从什么日期开始执行任务  20020-07-06 15:25:13
        period        执行各后续任务之间的时间间隔,单位是毫秒。定时器开始执行之后,每隔多少毫秒重复执行
*/

<br/>

TimerTask 类:由 Timer 执行的一次执行或重复执行的任务

java.util.TimerTask implements Runnable 接口

TimerTask 类是一个抽象类,无法直接创建

成员抽象方法:

// 此计时器任务要执行的操作。重写run方法,设置线程任务
void run()

<br/>

使用示例

import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Timer;
import java.util.TimerTask;

public class Demo01Timer {
    public static void main(String[] args) throws ParseException {
        show04();
    }
    
    /*
        在指定的毫秒值之后,执行指定的任务,只会执行一次
        void schedule(TimerTask task, long delay) 
    */
    private static void show01() {
        Timer timer = new Timer();
        timer.schedule(new TimerTask() {
            @Override
            public void run() {
                System.out.println("c4爆炸了!");
                timer.cancel();
            }
        }, 5000);
    }
    
    /*
        在指定的毫秒值之后,执行指定的任务,之后每隔固定的毫秒数重复执行定时任务
        void schedule(TimerTask task, long delay, long period)
     */
    private static void show02() {
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        Timer timer =new Timer();
        timer.schedule(new TimerTask() {
            @Override
            public void run() {
                System.out.println(sdf.format(new Date()));
            }
        }, 5000, 1000);
    }
    
    /*
        安排在指定的时间执行指定的任务,只会执行一次
        void schedule(TimerTask task, Date time) 
     */
    private static void show03() throws ParseException {
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        Date date = sdf.parse("2020-07-06 15:32:50");
        Timer timer = new Timer();
        timer.schedule(new TimerTask() {
            @Override
            public void run() {
                System.out.println("哈哈!");
                timer.cancel();
            }
        }, date);
    }

    /*
        安排指定的任务在指定的时间开始进行重复的固定延迟执行
        void schedule(TimerTask task, Date firstTime, long period) 
     */
    private static void show04() throws ParseException {
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        Date date = sdf.parse("2020-07-06 15:37:30");
        Timer timer  = new Timer();
        timer.schedule(new TimerTask() {
            @Override
            public void run() {
                System.out.println("嘿嘿,你中招了!");
            }
        }, date, 10);
    }
}
相关文章
|
3月前
|
资源调度 Java 调度
Spring Cloud Alibaba 集成分布式定时任务调度功能
定时任务在企业应用中至关重要,常用于异步数据处理、自动化运维等场景。在单体应用中,利用Java的`java.util.Timer`或Spring的`@Scheduled`即可轻松实现。然而,进入微服务架构后,任务可能因多节点并发执行而重复。Spring Cloud Alibaba为此发布了Scheduling模块,提供轻量级、高可用的分布式定时任务解决方案,支持防重复执行、分片运行等功能,并可通过`spring-cloud-starter-alibaba-schedulerx`快速集成。用户可选择基于阿里云SchedulerX托管服务或采用本地开源方案(如ShedLock)
122 1
|
3月前
|
Java
Spring5入门到实战------9、AOP基本概念、底层原理、JDK动态代理实现
这篇文章是Spring5框架的实战教程,深入讲解了AOP的基本概念、如何利用动态代理实现AOP,特别是通过JDK动态代理机制在不修改源代码的情况下为业务逻辑添加新功能,降低代码耦合度,并通过具体代码示例演示了JDK动态代理的实现过程。
Spring5入门到实战------9、AOP基本概念、底层原理、JDK动态代理实现
|
29天前
|
Java BI 调度
Java Spring的定时任务的配置和使用
遵循上述步骤,你就可以在Spring应用中轻松地配置和使用定时任务,满足各种定时处理需求。
123 1
|
1月前
|
存储 Java API
简单两步,Spring Boot 写死的定时任务也能动态设置:技术干货分享
【10月更文挑战第4天】在Spring Boot开发中,定时任务通常通过@Scheduled注解来实现,这种方式简单直接,但存在一个显著的限制:任务的执行时间或频率在编译时就已经确定,无法在运行时动态调整。然而,在实际工作中,我们往往需要根据业务需求或外部条件的变化来动态调整定时任务的执行计划。本文将分享一个简单两步的解决方案,让你的Spring Boot应用中的定时任务也能动态设置,从而满足更灵活的业务需求。
82 4
|
1月前
|
Java 关系型数据库 开发工具
idea创建不了spring2.X版本,无法使用JDK8,最低支持JDK17 , 如何用idea创建spring2.X版本,使用JDK8解决方案
本文提供了解决方案,如何在IDEA中创建Spring 2.X版本的项目并使用JDK8,尽管Spring 2.X已停止维护且IDEA不再直接支持,通过修改pom.xml或使用阿里云的国内源来创建项目。
73 0
idea创建不了spring2.X版本,无法使用JDK8,最低支持JDK17 , 如何用idea创建spring2.X版本,使用JDK8解决方案
|
4月前
|
资源调度 Java 调度
Spring Cloud Alibaba 集成分布式定时任务调度功能
Spring Cloud Alibaba 发布了 Scheduling 任务调度模块 [#3732]提供了一套开源、轻量级、高可用的定时任务解决方案,帮助您快速开发微服务体系下的分布式定时任务。
14932 30
|
2月前
|
Java 编译器 API
JDK8新特性--lambda表达式
JDK8的Lambda表达式是Java语言的一大进步。它为Java程序提供了更多的编程方式,让代码更加简洁,也让函数式编程的概念在Java中得到了体现。Lambda表达式与Java 8的其他新特性,如Stream API、新的日期时间API一起,极大地提高了Java编程的效率和乐趣。随着时间的流逝,Java开发者对这些特性的理解和应用将会越来越深入,进一步推动Java语言和应用程序的发展。
14 0
|
3月前
|
Java 开发者 Spring
Spring Boot实战宝典:揭秘定时任务的幕后英雄,让业务处理如流水般顺畅,轻松驾驭时间管理艺术!
【8月更文挑战第29天】在现代应用开发中,定时任务如数据备份、报告生成等至关重要。Spring Boot作为流行的Java框架,凭借其强大的集成能力和简洁的配置方式,为开发者提供了高效的定时任务解决方案。本文详细介绍了如何在Spring Boot项目中启用定时任务支持、编写定时任务方法,并通过实战案例展示了其在业务场景中的应用,同时提供了注意事项以确保任务的正确执行。
51 0
|
3月前
|
Dubbo Java 调度
揭秘!Spring Cloud Alibaba的超级力量——如何轻松驾驭分布式定时任务调度?
【8月更文挑战第20天】在现代微服务架构中,Spring Cloud Alibaba通过集成分布式定时任务调度功能解决了一致性和可靠性挑战。它利用TimerX实现任务的分布式编排与调度,并通过`@SchedulerLock`确保任务不被重复执行。示例代码展示了如何配置定时任务及其分布式锁,以实现每5秒仅由一个节点执行任务,适合构建高可用的微服务系统。
67 0
|
3月前
|
算法 Java
JDK8到JDK19版本升级的新特性问题之在JDK12中,switch表达式扩展带来了什么变化
JDK8到JDK19版本升级的新特性问题之在JDK12中,switch表达式扩展带来了什么变化