Spring Task 超详解版

目录

一、定时任务的理解

二、入门案例

三、Cron表达式

四、Cron实战案例

五、多线程案例


一、定时任务的理解

定时任务即系统在特定时间执行一段代码,它的场景应用非常广泛:

  • 购买游戏的月卡会员后,系统每天给会员发放游戏资源。
  • 管理系统定时生成报表。
  • 定时清理系统垃圾。

定时任务的实现主要有以下几种方式:

  1. Java自带的java.util.Timer类,这个类允许调度一个java.util.TimerTask任务。使用这种方式可以让程序按照某一个频度执行,但不能在指定时间运行。一般用的较少。
  2. Quartz。这是一个功能比较强大的的调度器,可以让程序在指定时间执行,也可以按照某一个频度执行,配置起来稍显复杂。
  3. Spring3.0以后自带Spring Task,可以将它看成一个轻量级的Quartz,使用起来比 Quartz简单许多,在课程中我们使用Spring Task实现定时任务

二、入门案例

创建SpringBoot项目,在启动类开启定时任务。

也就是在启动类上方添加@EnableScheduling注解即可开启定时任务,代码如下:

package com.example.springboottaskdemo;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.scheduling.annotation.EnableScheduling;

@SpringBootApplication
@EnableScheduling
public class SpringboottaskdemoApplication {

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

}

编写定时任务类 

@Component
public class MyTask {
  // 定时任务方法,每秒执行一次
  @Scheduled(cron="* * * * * *")
  public void task1() {
    SimpleDateFormat sdf = new SimpleDateFormat("HH:mm:ss");
    System.out.println(sdf.format(new Date()));
 }
}

启动项目,定时任务方法按照配置定时执行。 

OK,果然如此,每隔一秒输出当前时间

@Scheduled写在方法上方,指定该方法定时执行。常用参数如下:

  1. cron:cron表达式,定义方法执行的时间规则。
  2. fixedDelay:任务立即执行,之后每隔多久执行一次,单位是毫秒,上一次任务结束后计算下次执行的时间。

OK,先来一个案例,代码如下:任务结束后每隔五秒执行一次

// 立即执行,任务结束后五秒执行一次
    @Scheduled(fixedDelay = 5000)
    public void task1() throws InterruptedException {
        SimpleDateFormat sdf = new SimpleDateFormat("YYYY-MM-dd HH:mm:ss");
        System.out.println("Task1: "+sdf.format(new Date()));
    }

效果如下: 

OK,果然如此,注意这个是任务结束后每隔五秒,如果方法中间加了一个sleep方法,那么执行时间还要加上sleep里面的值,比如说中间加了一个sleep(1000),那么就会每隔6秒执行一次。

fixedRate:任务立即执行,之后每隔多久执行一次,单位是毫秒,上一次任务开始后计算下次执行的时间。

案例如下,代码如下:

// 立即执行,之后每五秒执行一次
    @Scheduled(fixedRate = 5000)
    public void task2() throws InterruptedException {
        SimpleDateFormat sdf = new SimpleDateFormat("YYYY-MM-dd HH:mm:ss");
        // 没有影响五秒输出一次
        Thread.sleep(1000);
        System.out.println("Task2: "+sdf.format(new Date()));
    }

 OK,看如下执行代码确实是不受到sleep影响的

initialDelay:项目启动后不马上执行定时器,根据initialDelay的值延时执行。 为了突出刚刚说的fixedDelay会受到sleep影响,这里配合fixedDelay来结合测试演示一下:

代码如下:

// 项目启动后三秒执行,之后每六秒执行一次
    @Scheduled(fixedDelay = 5000,initialDelay = 3000)
    public void task3() throws InterruptedException {
        SimpleDateFormat sdf = new SimpleDateFormat("YYYY-MM-dd HH:mm:ss");
        // 没有影响五秒输出一次
        Thread.sleep(1000);
        System.out.println("Task3: "+sdf.format(new Date()));
    }

OK,看运行结果也是隔了三秒才出现第一次打印时间,并且打印时间是隔六秒打印一次 

三、Cron表达式

Spring Task依靠Cron表达式配置定时规则。Cron表达式是一个字符串,分为6或7个域,每一个域代表一个含义,以空格隔开。有如下两种语法格式:

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

Seconds(秒):域中可出现 , - * / 四个字符,以及0-59的整数

  • * :表示匹配该域的任意值,在Seconds域使用 * ,表示每秒钟都会触发
  • , :表示列出枚举值。在Seconds域使用 5,20 ,表示在5秒和20秒各触发一次。
  • - :表示范围。在Seconds域使用 5-20 ,表示从5秒到20秒每秒触发一次
  • / :表示起始时间开始触发,然后每隔固定时间触发一次。在Seconds域使用 5/20 , 表示5秒触发一次,25秒,45秒分别触发一次。

Minutes(分):域中可出现 , - * / 四个字符,以及0-59的整数
Hours(时):域中可出现 , - * / 四个字符,以及0-23的整数
DayofMonth(日期):域中可出现 , - * / ? L W C 八个字符,以及1-31的整数

  • C :表示和当前日期相关联。在DayofMonth域使用 5C ,表示在5日后的那一天触发,且每月的那天都会触发。比如当前是10号,那么每月15号都会触发。
  • L :表示最后,在DayofMonth域使用 L ,表示每个月的最后一天触发。
  • W :表示工作日,在DayofMonth域用 15W ,表示最接近这个月第15天的工作日触发,如果15号是周六,则在14号即周五触发;如果15号是周日,则在16号即周一触发;如果15号是周二则在当天触发。

注:

  1. 该用法只会在当前月计算,不会到下月触发。比如在DayofMonth域用 31W ,31号是周日,那么会在29号触发而不是下月1号。
  2. 在DayofMonth域用 LW ,表示这个月的最后一个工作日触发。

Month(月份):域中可出现 , - * / 四个字符,以及1-12的整数或JAN-DEC的单词缩写
DayofWeek(星期):可出现 , - * / ? L # C 八个字符,以及1-7的整数或SUN-SAT 单词缩写,1代表星期天,7代表星期六

  • C :在DayofWeek域使用 2C ,表示在2日后的那一天触发,且每周的那天都会触发。比如当前是周一,那么每周三都会触发。
  • L :在DayofWeek域使用 L ,表示在一周的最后一天即星期六触发。在DayofWeek域使用 5L ,表示在一个月的最后一个星期四触发。
  • # :用来指定具体的周数, # 前面代表星期几, # 后面代表一个月的第几周,比如 5#3 表示一个月第三周的星期四。
  • ? :在无法确定是具体哪一天时使用,用于DayofMonth和DayofWeek域。例如在每月的20日零点触发1次,此时无法确定20日是星期几,写法如下: 0 0 0 20 * ? ;或者在每月的最后一个周日触发,此时无法确定该日期是几号,写法如下: 0 0 0 ? * 1L

Year(年份):域中可出现 , - * / 四个字符,以及1970~2099的整数。该域可以省略,表示每年都触发。

四、Cron实战案例

下面有常用的案例,大家可以参考一下

Cron实战案例
含义表达式
每隔5分钟触发一次0 0/5 * * * *
每小时触发一次0 0 * * * *
每天的7点30分触发0 30 7 * * *
周一到周五的早上6点30分触发0 30 6 ? * 2-6
每月最后一天早上10点触发0 0 10 L * ?
每月最后一个工作日的18点30分触发0 30 18 LW * ?
2030年8月每个星期六和星期日早上10点触发0 0 10 ? 8 1,7 2030
每天10点、12点、14点触发0 0 10,12,14 * * *
朝九晚五工作时间内每半小时触发一次0 0 0/30 9-17 ? * 2-6
每周三中午12点触发一次0 0 12 ? * 4
每天12点触发一次0 0 12 * * *
每天14点到14:59每分钟触发一次0 * 14 * * *
每天14点到14:59每5分钟触发一次0 0/5 14 * * *
每天14点到14:05每分钟触发一次0 0-5 14 * * *
每月15日上午10:15触发0 15 10 15 * ?
每月最后一天的上午10:15触发0 15 10 L * ?
每月的第三个星期五上午10:15触发0 15 10 ? * 6#3

好啦,通过这些大家应该就可以领悟了

五、多线程案例

Spring Task定时器默认是单线程的,如果项目中使用多个定时器,使用一个线程会造成效率低下。

比如说我们设置了两个定时任务,那么因为Spring Task是单线程,如果在第一个定时任务加了一个sleep方法,那么会等第一个方法响应后在执行第二个任务,就很浪费cpu运行时间。代码如下:

    @Scheduled(cron = "* * * * * *")
    public void task1() throws InterruptedException {
        System.out.println(Thread.currentThread().getId()+"线程执行任务1 - "+new SimpleDateFormat("HH:mm:ss").format(new Date());
        Thread.sleep(5000);
    }

    @Scheduled(cron = "* * * * * *")
    public void task2() throws InterruptedException {
        System.out.println(Thread.currentThread().getId()+"线程执行任务2 - "+new SimpleDateFormat("HH:mm:ss").format(new Date());
    }

执行效果如下:可以看到是先执行了任务2,但是他们都要隔五秒才能运行一次,因为通过线程号可以知道这是同一个线程。

因此任务1较浪费时间,会阻塞任务2的运行。此时我们可以给SpringTask配置线程池。代码如下:

package com.example.springboottaskdemo;

import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.annotation.SchedulingConfigurer;
import org.springframework.scheduling.config.ScheduledTaskRegistrar;

import java.util.concurrent.Executors;

@Configuration
public class SchedulingConfig implements SchedulingConfigurer {
    @Override
    public void configureTasks(ScheduledTaskRegistrar taskRegistrar) {
        // 创建线程池,设置五个线程
        taskRegistrar.setScheduler(Executors.newScheduledThreadPool(4));
    }
}

这样就不会出现阻塞问题了,因为两个任务不是同一个线程,接下来我们再次运行看看:

执行效果如上,确实不会影响到任务2的运行,但是如果定时任务过多,超过了配置的线程池的线程数量还是会运行错乱。

Ok,SpringBoot到这里就完结撒花了。

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

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

相关文章

基于姿态估计的3D动画生成

在本文中,我们将尝试通过跟踪 2D 视频中的动作来渲染人物的 3D 动画。 在 3D 图形中制作人物动画需要大量的运动跟踪器来跟踪人物的动作,并且还需要时间手动制作每个肢体的动画。 我们的目标是提供一种节省时间的方法来完成同样的任务。 我们对这个问题…

EasyMetagenome易宏基因组——简单易用的宏基因组分析流程-来自刘永鑫团队的秘密武器

原仓库地址如下,github有时候无法访问,等一段时间再试就行: YongxinLiu/EasyMetagenome: Easy Metagenome Pipeline (github.com) 相关文章,看文章更清晰这个可干啥: EasyAmplicon: An easy‐to‐use, open‐source…

JAVA高级-1

常用API 第一章 API 产品说明书 第二章 Scanner类(输入) 功能:获取键盘输入 package day7_12.demo01_Scanner;import java.util.Scanner; //1、导包 /* 功能:获取键盘输入引用类型一般使用步骤1、导包:impo…

深入了解汉字转拼音转换工具:原理与应用

一、引言 汉字作为世界上最古老、最具象形意的文字之一,承载了数千年的历史文明。然而,在现代信息技术环境下,汉字的输入、输出和检索等方面存在一定的局限性。拼音作为汉字的一种音标表达方式,能够有效地解决这些问题。本文将为…

JS利用时间戳倒计时案例

我们在逛某宝,或者逛某东时,我们时常看到一个倒计时,时间一到就开抢,这个倒计时是如何做的呢?让我为大家介绍一下。 理性分析一下: 1.用将来时间减去现在时间就是剩余的时间 2.核心:使用将来的时…

完全背包问题 非零基础

目录 之前学过一遍,但是12月2日再练忘光光了: 忘记点1 —— 为什么每个物品要遍历k件: 忘记点2 —— 数学优化: 之前学过一遍,但是12月2日再练忘光光了: 【模板】完全背包_牛客题霸_牛客网 (nowcoder.c…

智慧公厕新风系统是什么?具体作用?

大家好,你们可曾在公厕里遇到那个臭味怪兽,闻得让人头晕目眩?别怕,我们有一把利剑,叫做“智慧公厕新风系统”!不仅是空气净化器的升级版,还有一大堆高级功能等着你来领略! 1. 风清气…

Linux常用命令——awk命令

在线Linux命令查询工具 awk 文本和数据进行处理的编程语言 补充说明 awk是一种编程语言,用于在linux/unix下对文本和数据进行处理。数据可以来自标准输入(stdin)、一个或多个文件,或其它命令的输出。它支持用户自定义函数和动态正则表达式等先进功能…

1+x网络系统建设与运维(中级)-练习3

一.设备命名 AR1 [Huawei]sysn AR1 [AR1] 同理可得,所有设备的命名如上图所示 二.VLAN LSW1 [LSW1]vlan 10 [LSW1-vlan10]q [LSW1]int g0/0/1 [LSW1-GigabitEthernet0/0/1]port link-type access [LSW1-GigabitEthernet0/0/1]port default vlan 10 [LSW1-GigabitEt…

SQL数据库知识点总结

前后顺序可以任意颠倒,不影响库中的数据关系 关系数据库的逻辑性强而物理性弱,因此关系数据库中的各条记录前后顺序可以任意颠倒,不影响库中的数据关系 一名员工可以使用多台计算机(1:m),而一…

Knowledge Review(CVPR 2021)论文解析

paper:Distilling Knowledge via Knowledge Review official implementation:https://github.com/dvlab-research/ReviewKD 前言 识蒸馏将知识从教师网络转移到学生网络,可以提高学生网络的性能,作为一种“模型压缩”的方法被…

数据结构树,二叉树,堆

目录 ​编辑 1.树概念及结构 2. 树的表示 3.二叉树概念及结构 特殊的二叉树 二叉树的性质 ​编辑 二叉树选择题 二叉树的存储结构 4.堆的概念及结构 父亲孩子下标关系​编辑 堆的实现接口 堆结构体设计堆的初始化堆的销毁 堆的插入(附:向上调整算法) 堆…

[多线程]线程安全问题再讨论 - volatile

目录 1.引言 2.volatil关键字 2.1内存可见性 2.2指令重排序 1.引言 大家好,我是老cu,今天我们来继续聊聊线程安全问题 线程安全是我们在编程开发中遇到的非常常见,棘手 的问题.同时也是多线程部分很复杂的问题.为了线程安全我们要做很多努力.也要对线程安全部分的代码进行慎…

计算机网络的分类

目录 一、按照传输介质进行分类 1、有线网络 2、无线网络 二、按照使用者进行分类 1、公用网 (public network) 2、专用网(private network) 三、按照网络规模和作用范围进行分类 1、PAN 个人局域网 2、LAN 局域网 3、MAN 城域网 4、 WAN 广域网 5、Internet 因特…

【算法】直接插入排序

目录 1. 说明2. 举个例子3. java代码示例4. java示例截图 1. 说明 1.直接插入排序的方式和打牌一样,刚开始数组为空 2.拿到一个数字后从左到右将它与数组中的每一个数字进行比较,然后插入合适的位置 3.到最后,数组按照既定的顺序排序好 2. 举…

代码随想录算法训练营第五十三天【动态规划part14】 | 1143.最长公共子序列、1035.不相交的线、53. 最大子序和

1143.最长公共子序列 题目链接 力扣(LeetCode)官网 - 全球极客挚爱的技术成长平台 求解思路 动规五部曲 1.确定dp数组及其下标含义: dp[i][j]:长度为[0, i - 1]的字符串text1与长度为[0, j - 1]的字符串text2的最长公共子序…

Tensorflow的日志log记录

if OUTPUT_GRAPH:tf.summary.FileWriter("logs/", sess.graph)自动创建文件夹log

GEE:Sobel算子卷积和Roberts算子卷积对比

作者:CSDN @ _养乐多_ 本文介绍了Sobel算子卷积和Roberts算子卷积操作的代码,并进行了图像对比,可以观察到两个算子的细微差异。 文章目录 一、Sobel算子和Roberts算子对比二、完整代码三、代码链接一、Sobel算子和Roberts算子对比 详细介绍介绍参考《遥感数字图像处理教程…

基于搜索协议实现工业设备升级

目录 1、背景引入 2、技术分析 3、过程概述 4、服务器端流程 5、客户端流程 6、效果展示 7、源码 7.1 master(主控) 7.2 device(设备) 8、注意事项 1、背景引入 在工业生产中,设备的升级和维护是非常重要的…

Gossip 协议

Gossip 协议 背景 在分布式系统中,不同的节点进行数据/信息共享是一个基本的需求。 一种比较简单粗暴的方法就是 集中式发散消息,简单来说就是一个主节点同时共享最新信息给其他所有节点,比较适合中心化系统。这种方法的缺陷也很明显&…