spring 定时任务Scheduler和异步任务Async

1. 概述

Spring框架分别通过TaskExecutor和TaskScheduler接口为任务提供异步执行和调度。

ThreadPoolTaskScheduler(继承自TaskScheduler)ThreadPoolTaskExecutor(继承自TaskExecutor)备注
含义任务调度器,定时任务线程池执行器,异步任务
用途主要用于在指定的时间间隔内执行任务或定时任务用于执行异步任务和多线程任务
线程池基于java.util.concurrent.ScheduledExecutorService基于 java.util.concurrent.ThreadPoolExecutorthreadPool
默认配置springboot默认线程池大小为1springboot默认线程池大小8
注解@EnableScheduling
@Scheduled
@EnableAsync
@EnableScheduling
@Async
@Scheduled
1注解的方法必须是public方法


2.方法一定要从另一个类中调用,也就是从类的外部调用,类的内部调用是无效的,因为注解的实现都是基于Spring的AOP,而AOP的实现是基于动态代理模式实现的。那么注解失效的原因就很明显了,有可能因为调用方法的是对象本身而不是代理对象,因为没有经过Spring容器。


3. 异步方法使用注解@Async的返回值只能为void或者Future
函数返回值函数可以有返回值函数没有返回值
异步执行单个任务同步执行。



当执行时间大于我们间隔时间时,上一次任务执行完成后才会开始下一个任务。
非阻塞异步执行。


每次执行定时任务都会新开一个线程,即使之前的任务没有完成。

2. 定时任务Scheduler

定时任务会创建线程池ScheduledThreadPoolExecutor,用于执行任务。springboot默认Scheduler线程池corePoolSize=1

2.1 定时任务 - 相关注解及使用方法(一个简单的例子)

spring定时任务使用非常简单,只需要添加两个注解@EnableScheduling,@Scheduled

1.@EnableScheduling:在spring管理的类上添加都可以,通常添加在启动类上

@EnableScheduling
@SpringBootApplication
public class Application {
    public static void main(String[] args) {
       SpringApplication.run(Application.class, args);    }
}

2. @Scheduled: 在定时任务的实现方法上添加该注解(@Scheduled注解的详细介绍见4. @Scheduled参数详解

@Slf4j
@Service
public class ScheduledTask {

    @Scheduled(cron ="* * * * * *")
    public int ScheduledTaskOne() throws InterruptedException {
        log.info("------- ScheduledTaskOne start...");
        Thread.sleep(10000L);
        log.info("------- ScheduledTaskOne done.");
        return 1;
    }
}

如上任务配置了每秒运行一次。由于函数中执行了 Thread.sleep(10000L),可以知道该任务执行完成大约10秒。

启动服务,可以看到如下log:

2023-10-17 11:38:00.012  INFO 18620 --- [   scheduling-1] com.shirley.spring.tasks.ScheduledTask   : ------- ScheduledTaskOne start...
2023-10-17 11:38:10.024  INFO 18620 --- [   scheduling-1] com.shirley.spring.tasks.ScheduledTask   : ------- ScheduledTaskOne done.
2023-10-17 11:38:11.002  INFO 18620 --- [   scheduling-1] com.shirley.spring.tasks.ScheduledTask   : ------- ScheduledTaskOne start...
2023-10-17 11:38:21.004  INFO 18620 --- [   scheduling-1] com.shirley.spring.tasks.ScheduledTask   : ------- ScheduledTaskOne done.
2023-10-17 11:38:22.003  INFO 18620 --- [   scheduling-1] com.shirley.spring.tasks.ScheduledTask   : ------- ScheduledTaskOne start...

从log中记录的时间可以看出,虽然@Scheduled配置了每秒启动一次,但第二次执行ScheduledTaskOne还是会等到第一次执行完毕后才运行,而不是根据cron配置每秒运行一次。这和@Async的行为是不同的!

2.2 定时任务 - 修改配置

2.2.1 查看默认配置

spring提供了一些配置项对scheduler进行配置。这里介绍一个查看spring配置项的方法。

1. 通过IDEL(如Intellij IDEA)找到依赖项中的spring-boot-autoconfigure:***,这个就是springboot自动配置模块。

2. 在org.springframework.boot.autoconfigure.task package下,有两个*Properties类,这两个类就是配置项对应的Bean。TaskSchedulingProperties为Scheduler的配置,TaskExecutionProperties为异步任务的相关配置。

3. 查看TaskExecutionProperties源码(部分代码如下),可以看出scheduler相关配置以spring.task.scheduling 开头。springboot会通过类中各属性的set方法给属性赋值。例如,配置项spring.task.scheduling.threadNamePrefix 会通过TaskSchedulingProperties.setThreadNamePrefix(String threadNamePrefix) 函数复制给threadNamePrefix

// 配置项前缀
@ConfigurationProperties("spring.task.scheduling")
public class TaskSchedulingProperties {

    private final Pool pool = new Pool();
    private final Shutdown shutdown = new Shutdown();

     // 线程名称前缀
    private String threadNamePrefix = "scheduling-";

    public static class Pool {
        // 默认线程池大小为1
        private int size = 1;

    }

    public static class Shutdown {
         // 当线程池调用shutdown时,是否等待任务完成
        private boolean awaitTermination;
         // 等待任务完成最长时间。
        private Duration awaitTerminationPeriod;
    }
}

2.2.2 修改配置

根据TaskSchedulingProperties类,我们可以得到任务调度相关配置项有哪些。如下是一份完整的配置(如下配置文件为yaml格式,如果配置文件为.properties, 请自行将yaml转为properties。)

spring:
  task:
    scheduling:
      # 线程名前缀  
      threadNamePrefix: shirley-scheduler-
      pool:
        # 线程池大小。corePoolSize
        size: 5
      shutdown:
        # 当线程池shutdown时,是否等待已分配的任务执行完成
        awaitTermination: true
        # shutdown后,等待任务完成
的最长时间
        awaitTerminationPeriod: "2s"

在日常开发中,通常我们只需要配置spring.task.scheduling.pool.size

3. 线程池执行器(异步任务) Async

定时任务会创建线程池用于执行任务。springboot 默认Async线程池 corePoolSize=8

3.1 异步任务 - 相关注解及使用方法(一个简单的例子)

1. 添加注解@EnableAsync,@EnableScheduling启动异步任务

@EnableAsync
@EnableScheduling
@SpringBootApplication
public class Application {

    public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
    }

}

2. 在定时任务的实现方法上添加该注解@Async,@Scheduled

@Slf4j
@Service
public class AsyncTask {
    @Async
    @Scheduled(cron = "* * * * * *")
    public void AsyncTaskOne() throws InterruptedException {
        log.info("------- AsyncTaskOne start...");
        Thread.sleep(10000L);
        log.info("------- AsyncTaskOne done.");
    }
}

如上任务配置了每秒运行一次。由于函数中执行了 Thread.sleep(10000L),可以知道该任务执行完成大约10秒。

启动服务,可以看到如下log

2023-Oct-18 12:34:07.022 INFO  [task-1] c.s.s.t.AsyncTask:14 - ------- AsyncTaskOne start...
2023-Oct-18 12:34:08.001 INFO  [task-2] c.s.s.t.AsyncTask:14 - ------- AsyncTaskOne start...
2023-Oct-18 12:34:09.013 INFO  [task-3] c.s.s.t.AsyncTask:14 - ------- AsyncTaskOne start...
2023-Oct-18 12:34:10.008 INFO  [task-4] c.s.s.t.AsyncTask:14 - ------- AsyncTaskOne start...
2023-Oct-18 12:34:11.008 INFO  [task-5] c.s.s.t.AsyncTask:14 - ------- AsyncTaskOne start...
2023-Oct-18 12:34:12.005 INFO  [task-6] c.s.s.t.AsyncTask:14 - ------- AsyncTaskOne start...
2023-Oct-18 12:34:13.003 INFO  [task-7] c.s.s.t.AsyncTask:14 - ------- AsyncTaskOne start...
2023-Oct-18 12:34:14.016 INFO  [task-8] c.s.s.t.AsyncTask:14 - ------- AsyncTaskOne start...
2023-Oct-18 12:34:17.035 INFO  [task-1] c.s.s.t.AsyncTask:16 - ------- AsyncTaskOne done.
2023-Oct-18 12:34:17.037 INFO  [task-1] c.s.s.t.AsyncTask:14 - ------- AsyncTaskOne start...

从log中可以看出,同时有8个相同的任务同时执行,线程分别为[task-1] ~ [task8]。

3.2 异步任务 - 修改配置

通过查看spring-boot-autoconfigure:***.jar 下类 org.springframework.boot.autoconfigure.task.TaskExecutionProperties,可以得知异步任务相关配置项。配置项说明如下:

spring:
  task:
    execution:
      # 线程名前缀  
      threadNamePrefix: shirleyTask-
     
      # 线程池相关配置
      pool:
        # 对应线程池的corePoolSize
        coreSize: 8
        # 对应线程池 workQueue的大小。
        queueCapacity: 100
        # 对应线程池的maxPoolSize
        maxSize: 100
        # 对应线程池 keepAliveTime,TimeUnit.SECONDS
        keepAlive: 60s
        # 线程池coreThread空闲时间超过keepAlive后,是否terminated。
        allowCoreThreadTimeout: true
      
      shutdown:
        # 当线程池shutdown时,是否等待已分配的任务执行完成
        awaitTermination: false
        # shutdown后,等待任务完成
的最长时间
        awaitTerminationPeriod: "10s"

3.3 自定义线程池

如果不想用spring默认的线程池,比如希望更改默认线程池拒绝策略(AbortPolicy),也可以自定义线程池。

自定义线程池只需要两步:

1. 定义一个TaskExecutor类型的spring bean。如下例子中,定义了名为"shirleyExecutor" 的TaskExecutor

    @Bean("shirleyExecutor")
    public Executor shirleyExecutor() {
        ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
        //最大线程数
        executor.setMaxPoolSize(5);
        //核心线程数
        executor.setCorePoolSize(5);
        //任务队列的大小
        executor.setQueueCapacity(10);
        //线程前缀名
        executor.setThreadNamePrefix("shirley-exe-");
        //线程存活时间
        executor.setKeepAliveSeconds(10);

        /**
         * 拒绝处理策略
         * CallerRunsPolicy():交由调用方线程运行,比如 main 线程。
         * AbortPolicy():直接抛出异常。
         * DiscardPolicy():直接丢弃。
         * DiscardOldestPolicy():丢弃队列中最老的任务。
         */
        executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
        executor.initialize();
        return executor;
    }

2. 在@Async注解上指定自定义的Bean

    @Async("shirleyExecutor")
    @Scheduled(cron = "* * * * * *")
    public void shirleyExeTask() throws InterruptedException {
        log.info("------- shirleyExeTask start...");
        Thread.sleep(10000L);
        log.info("------- shirleyExeTask done.");
    }

注意:Async执行器方法只能返回void或Further

运行后,可以从log看出已经使用了自定义的Executor。在这个例子中,threadName已经变成了shirley-exe-*

2023-Oct-18 17:21:39.014 INFO  [shirley-exe-1] c.s.s.t.AsyncTask:38 - ------- shirleyExeTask start...
2023-Oct-18 17:21:40.011 INFO  [shirley-exe-2] c.s.s.t.AsyncTask:38 - ------- shirleyExeTask start...
2023-Oct-18 17:21:41.010 INFO  [shirley-exe-3] c.s.s.t.AsyncTask:38 - ------- shirleyExeTask start...

4. @Scheduled参数详解

spring 异步任务Async和定时任务Scheduler都可以通过@Scheduled注解进行调度。@Scheduled注解有三种方式配置调度频率:

  • cron: cron表达式,和Linux的cron表达式类似。
  • fixedRate:每隔fixedRate执行一次任务。如@Scheduled(fixedRate = 5, timeUnit = TimeUnit.SECONDS)表示每隔5秒执行一次任务。
  • fixedDelay:上一次运行结束和下一次运行开始之间的时间间隔。如 @Scheduled(fixedDelay = 5, timeUnit = TimeUnit.SECONDS) 表示上一次运行结束后5秒,开始下一次执行

注意,如果通过cron,fixedRate配置执行频率,Scheduler和Async的行为不同:

  • 在定时任务Scheduler模式下,如果任务执行时间超过fixedRate,则会在上一个任务执行结束后才开始下一次执行;
  • 在Async定时任务模式下,即使上一次任务还没有结束,

4.1 cron说明

cron表达式一共6位,分别表示:秒,分,时,日期,月,星期

 ┌───────────── second (0-59)
 │ ┌───────────── minute (0 - 59)
 │ │ ┌───────────── hour (0 - 23)
 │ │ │ ┌───────────── day of the month (1 - 31)
 │ │ │ │ ┌───────────── month (1 - 12) (or JAN-DEC)
 │ │ │ │ │ ┌───────────── day of the week (0 - 7)
 │ │ │ │ │ │          (0 or 7 is Sunday, or MON-SUN)
 │ │ │ │ │ │
 * * * * * *
 
* 第一位,表示秒,取值0-59
* 第二位,表示分,取值0-59
* 第三位,表示小时,取值0-23
* 第四位,日期天/日,取值1-31
* 第五位,日期月份,可以为:1-12;也可以表示为英文月份缩写:JAN~DEC
* 第六位,星期,可以为:0-7(0或7表示星期天); 也可以为英文星期缩写:MON~SUN

表达式说明:

(*)星号:可以理解为每的意思,每秒,每分,每天,每月,每年...

(?)问号:问号只能出现在日期和星期这两个位置,表示这个位置的值不确定,每天3点执行,所以第六位星期的位置,我们是不需要关注的,就是不确定的值。同时:日期和星期是两个相互排斥的元素,通过问号来表明不指定值。比如,1月10日,比如是星期1,如果在星期的位置是另指定星期二,就前后冲突矛盾了。

(-)减号:表达一个范围,如在小时字段中使用“10-12”,则表示从10到12点,即10,11,12

(,)逗号:表达一个列表值,如在星期字段中使用“1,2,4”,则表示星期一,星期二,星期四

(/)斜杠:如:x/y,x是开始值,y是步长,比如在第一位(秒) 0/15就是,从0秒开始,每15秒,最后就是0,15,30,45,60    另:*/y,等同于0/y

英文月份/星期缩写:可以用月份/星期 英文单词的前三个字母表示月份和星期几,大小写不敏感。

(L)最后一个:日期和星期如果包含L字母,表示最后一天。如日期使用“L”表示月的最后一天;“L-n”表示 n号到月末。

(LW)最后一个工作日(周一到周五,不是中国的法定工作日)

(d#n)第n个星期d

一些有用的cron表达式:

0 0 3 * * ?             每天3点执行
0 5 3 * * ?             每天3点5分执行
0 5 3 ? * *             每天3点5分执行,与上面作用相同
0 5/10 3 * * ?          每天3点的 5分,15分,25分,35分,45分,55分这几个时间点执行
0 10 3 ? * 1            每周星期天,3点10分 执行,注:1表示星期天
0 10 3 ? * 1#3          每个月的第三个星期一执行,#号只能出现在星期的位置
0 0 9-17 * * MON-FRI    每月周一到周五9点到17点
0 0 0 25 12 ?           每个圣诞节(12月25日)0点
0 0 0 L * *             每个月最后一天0点
0 0 0 L-3 * *           每月3号到月底的0点
0 0 0 1W * *            每月第一个工作日的0点
0 0 0 LW * *            每月最后一个工作日的0点
0 0 0 * * 5L            每个月最后一个周五的0点
0 0 0 * * THUL          每月最后一个星期四0点
0 0 0 ? * 5#2           每月第二个星期五0点
0 0 0 ? * MON#1         每月第一个星期一0点

4.1.1 cron宏

由于0 0 * * * * 的可读性比较差,spring定义了一些宏,替代常见的cron表达式,比如:@Scheduled(cron = "@hourly").spring支持的宏如下表:

含义
@yearly 或 @annually每年一次 (0 0 0 1 1 *)
@monthly每月一次(0 0 0 1 * *)
@weekly每周一次 (0 0 0 * * 0)
@daily (or @midnight)每天一次 (0 0 0 * * *)
@hourly每小时一次 (0 0 * * * *)

示例代码:

    // 每小时运行一次
    @Scheduled(cron = "@hourly")
    public void shirleyExeTask() throws InterruptedException {
        log.info("------- shirleyExeTask start...");
        Thread.sleep(10000L);
        log.info("------- shirleyExeTask done.");

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mfbz.cn/a/258460.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

MySQL 8.0 InnoDB Tablespaces之File-per-table tablespaces(单独表空间)

文章目录 MySQL 8.0 InnoDB Tablespaces之File-per-table tablespaces(单独表空间)File-per-table tablespaces(单独表空间)相关变量:innodb_file_per_table使用TABLESPACE子句指定表空间变量innodb_file_per_table设置…

以存算一体芯片加速汽车智能化进程,后摩智能带来更优解?

汽车产业的长期价值锚点已悄然变化,催生出新的商业机遇。 过去,在燃油车市场,燃油经济性和品牌认知度等是重要的消费决策因素和资本价值衡量标准,但在新能源时代,产业价值聚焦在两方面,一是电动化&#xf…

<一>Python变量以及数据类型、命名规则及输出输入语句

一、变量的定义 定义变量的语法为:变量名变量值。(这里的 作用是赋值。) 定义变量后可以使用变量名来访问变量值。 变量就是用来‘盛放’数据的容器。 二、变量的类型 三、查看数据类型 如果临时想要查看一个变量存储的数据类型,可以使用 type(变量…

企业特别大的文件用什么传比较快,企业快速传输大文件必备神器

对于企业而言,不论是内部的文件交换、协作、备份,还是外部的文件分享、投标、审计,都需要高效、稳定、安全的文件传输方式。特别是对于一些特别大的文件,如视频、音频、图片、设计图、数据库等,如果传输速度慢、不稳定…

Flutter实现丝滑的滑动删除、移动排序等-Dismissible控件详解

文章目录 Dismissible 简介使用场景常用属性基本用法举例注意事项 Dismissible 简介 Dismissible 是 Flutter 中用于实现可滑动删除或拖拽操作的一个有用的小部件。主要用于在用户对列表项或任何其他可滑动的元素执行删除或拖动操作时,提供一种简便的实现方式。 使…

什么店生意好?C++采集美团商家信息做数据分析

最近遇到几个朋友,想要一起合伙投资一个实体店,不问类型,就看哪类产品相对比较受欢迎。抛除地址位置,租金的影响,我们之谈产品。因此,我熬了几个通宵,写了这么一段爬取美团商家商品信息的数据并…

vue中使用minio上传文件

创建一个 文件getOssClient import { getOssSetting } from "/api/common"; import Vue from "vue"; import { getCookies, getLocal } from "/utils/auth"; // get token from cookie export async function getStsToken() {//从后台获取stsTo…

澳鹏干货解答!“关于机器学习的十大常见问题”

探索机器学习的常见问题,了解机器学习和人工智能的基本概念、原理、发展趋势、用途、方法和所需的数据要求从而发掘潜在的商机。 什么是机器学习? 机器学习即教授机器如何学习的过程,为机器提供指导,帮助它们自己开发逻辑&#…

STM32G030C8T6:使用按键控制LED亮灭(外部中断)

本专栏记录STM32开发各个功能的详细过程,方便自己后续查看,当然也供正在入门STM32单片机的兄弟们参考; 本小节的目标是,系统主频64 MHZ,采用高速外部晶振,通过KEY1 按键的PA0 引脚配置成中断输入引脚,PB9引…

SSH的交互原理(wireshark的分析)

SSH的交换原理(wireshark篇) 首先要想了解ssh的交换原理,必须要先了解他的加密方式,他的加密方式是对称加密,和公钥加密。什么意思呢? 首先我们向服务器发送一个请求,然后服务器会发给我们他的…

【Linux】进程周边006之进程地址空间

👀樊梓慕:个人主页 🎥个人专栏:《C语言》《数据结构》《蓝桥杯试题》《LeetCode刷题笔记》《实训项目》《C》《Linux》 🌝每一个不曾起舞的日子,都是对生命的辜负 目录 前言 1.程序地址空间 1.1验证地址…

LeetCode 每日一题 Day 17 || 二分

1901. 寻找峰值 II 一个 2D 网格中的 峰值 是指那些 严格大于 其相邻格子(上、下、左、右)的元素。 给你一个 从 0 开始编号 的 m x n 矩阵 mat ,其中任意两个相邻格子的值都 不相同 。找出 任意一个 峰值 mat[i][j] 并 返回其位置 [i,j] 。 你可以假设整个矩阵周…

怎么检测DC-DC电源模块稳定性?电源测试系统测试有什么优势?

DC-DC电源模块稳定性测试 稳定性是衡量DC电源模块的重要指标,电源模块的稳定性直接影响着电源产品和设备的工作稳定性。DC-DC电源模块的稳定性,可以通过检测输出电压、输出电流、负载、波形、效率等参数来评估。 1. 静态测试方法 静态测试是通过直流电压…

sparksql介绍

1.1 SparkSQL介绍 SparkSQL,顾名思义,就是Spark生态体系中的构建在SparkCore基础之上的一个基于SQL的计算模块。 SparkSQL的前身不叫SparkSQL,而叫Shark,最开始的时候底层代码优化,sql的解析、执行引擎等等完全基于H…

基于ssm酒店客房管理系统论文

摘 要 现代经济快节奏发展以及不断完善升级的信息化技术,让传统数据信息的管理升级为软件存储,归纳,集中处理数据信息的管理方式。本酒店客房管理系统就是在这样的大环境下诞生,其可以帮助管理者在短时间内处理完毕庞大的数据信息…

基于CNN+数据增强+残差网络Resnet50的少样本高准确度猫咪种类识别—深度学习算法应用(含全部工程源码)+数据集+模型(五)

系列文章目录 基于CNN数据增强残差网络Resnet50的少样本高准确度猫咪种类识别—深度学习算法应用(含全部工程源码)数据集模型(一) 基于CNN数据增强残差网络Resnet50的少样本高准确度猫咪种类识别—深度学习算法应用(含全部工程源码)数据集模型&#xf…

seaborn库图形进行数据分析(基于tips数据集)

目录 一、相关性 二、变量分析 三、统计数 四、 特征值分布 五、多变量 Seaborn 是一个基于 matplotlib 的数据可视化库,可以用来绘制各种统计图表,包括散点图、条形图、折线图、箱线图等。Seaborn 提供了一些用于美化图表的默认样式和颜色主题&am…

macOS 安装 oh-my-zsh 后 node 报错 command not found : node

最近为了让终端中显示 git 分支的名称,安装了 oh-my-zsh ,安装之后呢,我原先安装的 Volta、 node 都没法用了,报错如下: 这时候粗略判断应该是系统变量出了问题,oh-my-zsh 的变量文件是 ~/.zshrc&#xff0…

旅游景区项目信息化建设运营方案:PPT47页,附下载

关键词:智慧景区解决方案,智慧景区建设,智慧景区开发与管理,智慧景区建设的意义,智慧景区管理 一、旅游景区项目信息化建设背景 1、旅游业发展迅速:随着旅游业的不断发展,游客对旅游体验的需求…

简历摘要:它是什么、为什么重要以及如何编写

然而,在这里,你有绝佳的机会用自己的语言总结你最伟大的职业品质——就像用文字创作一幅自画像一样。如果做得好,你的简历摘要可以让你的简历引人注目,立即引起招聘经理的注意。但如果做得不好,可能会立即让人倒胃口。…