手撸XXL-JOB(一)——定时任务的执行

news2024/11/19 12:32:09

SpringBoot执行定时任务

对于定时任务的执行,SpringBoot提供了三种创建方式:
1)基于注解(@Scheduled)
2)基于接口(SchedulingConfigurer)
3)基于注解设定多线程定时任务

基于Scheduled注解

首先我们创建一个SpringBoot项目,然后引入spring-boot-starter-web依赖,在启动类上添加EnableScheduling注解开启定时任务管理。

package com.yang.demo1;

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

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

然后我们创建一个定时任务,并使用Scheduled注解

package com.yang.demo1;

import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;

import java.text.SimpleDateFormat;
import java.util.Date;

@Component
public class DemoTask {
    @Scheduled(cron = "0/5 * * * * ?") // 每5秒执行一次
    public void execute() {
        SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        System.out.println("hello world: " + df.format(new Date()));
    }
}

启动项目,执行结果如下所示,每隔5秒,便会执行一次定时任务。
image.png
Scheduled注解类的源码如下:

package org.springframework.scheduling.annotation;
 
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Repeatable;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
 
@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 "";
}

除了刚才代码中使用到的cron参数,我们还可以使用fixedDelay和fixedRate等参数。fixedDelay和fixedRate很相似,但又略有不同,其中fixedDelay表示在某次任务执行完毕后,间隔fixedDelay的时间再执行,而fixedRate表示在某次任务执行开始后,间隔fixedRate的时间再执行。
我们对这两个参数,添加对应的测试方法:

  @Scheduled(fixedDelay = 5000)
    public void executeFixedDelay() {
        SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        System.out.println("executeFixedDelay开始执行了==========" + df.format(new Date()));
        try {
            Thread.sleep(2000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    @Scheduled(fixedRate = 5000)
    public void executeFixedRate() {
        SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        System.out.println("executeFixedRate开始执行了==========" + df.format(new Date()));
        try {
            Thread.sleep(2000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

启动项目,执行结果如下。我们可以看到,executeFixedDelay在任务执行完毕后,间隔5秒才执行下一个任务,也就是说,两个任务之间间隔7秒,而executeFixedRate在任务执行开始后,间隔5秒执行下一个任务,也就是两个任务之间间隔5秒。综上所示,fixedDelay和fixedRate之间的区别,其实就在于计算两个任务执行间隔时,需不需要考虑任务的执行时长。
image.png

基于SchedulingConfigurer接口

首先我们在数据库中创建t_cron表,并添加数据:

DROP TABLE IF EXISTS t_cron;
CREATE TABLE t_cron  (
  cron_id VARCHAR(30) NOT NULL PRIMARY KEY,
  cron VARCHAR(30) NOT NULL  
);
 
INSERT INTO t_cron VALUES ('1', '0/5 * * * * ?');

然后修改pom.xml,加上数据库的相关依赖:

  <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
        </dependency>
        <dependency>
            <groupId>com.baomidou</groupId>
            <artifactId>mybatis-plus-boot-starter</artifactId>
            <version>3.4.3</version>
        </dependency>

修改配置文件:

spring:
  datasource:
    username: root
    password: 3fa4d180
    url: jdbc:mysql://localhost:3306/test01?useSSL=false&serverTimezone=UTC
    driver-class-name: com.mysql.cj.jdbc.Driver
mybatis-plus:
  configuration:
    log-impl: org.apache.ibatis.logging.stdout.StdOutImpl

创建对应的mapper:

package com.yang.demo1.mapper;

import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Select;

@Mapper
public interface CronMapper {
    @Select("select cron from t_cron limit 1")
    String getCron();
}

然后,我们编写定时任务,通过读取我们在数据库设置好的执行周期,来执行定时任务,代码如下:

package com.yang.demo1;

import com.yang.demo1.mapper.CronMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.annotation.EnableScheduling;
import org.springframework.scheduling.annotation.SchedulingConfigurer;
import org.springframework.scheduling.config.ScheduledTaskRegistrar;
import org.springframework.scheduling.support.CronTrigger;

import java.time.LocalDateTime;

@Configuration
@EnableScheduling
public class DynamicScheduleConfigurer implements SchedulingConfigurer {
    @Autowired
    private CronMapper cronMapper;

    @Override
    public void configureTasks(ScheduledTaskRegistrar taskRegistrar) {
        taskRegistrar.addTriggerTask(
                () -> execute(),
                triggerContext -> {
                    String cron = cronMapper.getCron();
                    if (StringUtils.isEmpty(cron)) {
                        throw new RuntimeException("cron 格式有误");
                    }
                    return new CronTrigger(cron).nextExecutionTime(triggerContext);
                }
        );
    }

    private void execute() {
        System.out.println("hello world:" + new Date());
    }
}

执行结果如下所示,一开始的cron是每5秒执行一次,后来我们修改cron,改为10秒执行一次,此时任务的执行频率,也随之我们数据库的修改动态地进行了调整。
image.png

EnableAsync注解

对于Scheduled,默认为单线程,当开启多个任务时,任务地执行实际会受到上一个任务执行时间地影响,所以需要使用Async注解,通过该注解开启多线程使任务之间不会相互影响。

ScheduledExecutorService执行定时任务

SpringBoot执行定时任务很简单,但是如果我们不使用SpringBoot,又该如何启动定时任务?这个时候,我们就可以使用ScheduledExecutorService接口,这个接口用于在一些预定义的延迟之后运行任务或定期运行任务。我们可以通过Executors类的工厂方法实例化ScheduledExecutorService,如下:

        ScheduledExecutorService scheduledExecutorService = Executors.newSingleThreadScheduledExecutor();

在ScheduledExecutorService接口中,有三个主要方法:
1)schedule:允许在指定的延迟后执行一次任务。
2)scheduleAtFixedRate:允许在指定的初始延迟后执行任务,然后以一定的周期重复执行,其中period参数用于指定两个任务的开始时间之间的间隔时间,因此任务执行的频率是固定的。
3)scheduleWithFixedDelay:类似于scheduleAtFixedRate,它也重复执行给定的任务,单period参数用于指定前一个任务的结束和下一个任务的开始之间的间隔时间,也就是指定下一个任务延时多久后才执行,执行频率可能会有所不同,具体取决于执行任务给定任务所需的时间。

schedule方法
package com.yang.demo2;

import java.util.Date;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;

public class ScheduledExecutorServiceMain {
    public static void main(String[] args) {
        ScheduledExecutorService scheduledExecutorService = Executors.newScheduledThreadPool(1);
        scheduledExecutorService.schedule(() -> {
            System.out.println("Hello World:" + new Date());
        }, 1, TimeUnit.SECONDS);

        try {
            Thread.sleep(20000);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
        scheduledExecutorService.shutdown();
        try {
            if (!scheduledExecutorService.awaitTermination(10, TimeUnit.SECONDS)) {
                scheduledExecutorService.shutdownNow();
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

结果如下:在延迟一秒后,开始执行任务,且只执行一次。
image.png

scheduleAtFixRate

当我们需要在固定延迟后,定期执行任务时,可以使用scheduleAtFixedRate方法,如下所示,每隔2秒执行相同的任务

public static void main(String[] args) {
        ScheduledExecutorService scheduledExecutorService = Executors.newScheduledThreadPool(1);
        scheduledExecutorService.scheduleAtFixedRate(() -> {
            System.out.println("Hello World:" + new Date());
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }, 0, 2, TimeUnit.SECONDS);

        try {
            Thread.sleep(20000);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
        scheduledExecutorService.shutdown();
        try {
            if (!scheduledExecutorService.awaitTermination(10, TimeUnit.SECONDS)) {
                scheduledExecutorService.shutdownNow();
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

结果如下:
image.png
如果任务执行时间比间隔时间长,那么scheduledExecutorService将等待当前任务执行完毕后再开始执行下一个任务,我们修改代码如下:

 ScheduledExecutorService scheduledExecutorService = Executors.newScheduledThreadPool(4);
        scheduledExecutorService.scheduleAtFixedRate(() -> {
            System.out.println("Hello World:" + new Date());
            try {
                Thread.sleep(3000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }, 0, 2, TimeUnit.SECONDS);

        try {
            Thread.sleep(20000);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
        scheduledExecutorService.shutdown();
        try {
            if (!scheduledExecutorService.awaitTermination(10, TimeUnit.SECONDS)) {
                scheduledExecutorService.shutdownNow();
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

结果如下,每隔3秒执行一次任务
image.png

scheduleWithFixedDelay方法

如果任务之间必须具有固定长度的延迟,那么可以使用scheduleWithFixedDelay方法。

  public static void main(String[] args) {
        ScheduledExecutorService scheduledExecutorService = Executors.newScheduledThreadPool(4);
        scheduledExecutorService.scheduleWithFixedDelay(() -> {
            System.out.println("Hello World:" + new Date());
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }, 0, 2, TimeUnit.SECONDS);

        try {
            Thread.sleep(20000);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
        scheduledExecutorService.shutdown();
        try {
            if (!scheduledExecutorService.awaitTermination(10, TimeUnit.SECONDS)) {
                scheduledExecutorService.shutdownNow();
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

在上述代码中,任务执行时长需要1秒,然后设置的间隔时间为2秒,因此,我们可以看到,每隔任务之间,间隔3秒执行一次
image.png

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

MySQL表的基本操作

表 创建表 comment是添加一个注释 语法&#xff1a; 说明&#xff1a; field 表示列名 datatype 表示列的类型 character set 字符集&#xff0c;如果没有指定字符集&#xff0c;则以所在数据库的字符集为准 collate 校验规则&#xff0c;如果没有指定校验规则&#xff0c;则…

C++:编程领域的全能王者

在编程语言的海洋中&#xff0c;C以其全面而强大的功能&#xff0c;犹如一位全能王者&#xff0c;屹立不倒。它不仅在科技领域有着广泛的应用&#xff0c;更在推动社会进步、促进人类创新方面发挥着至关重要的作用。 一、C&#xff1a;编程界的璀璨明星 C自诞生以来&#xff…

Linux使用脚本删除多个版本的jar包

问题描述&#xff1a;在进行测试的过程中发现&#xff0c;有一个导出xls文件的功能&#xff0c;文件正常到导出来了&#xff0c;但是页面上显示的是中文&#xff0c;但是导出来的xls文件取的确是数据库的存值&#xff0c;没有转换 前端一看代码说没问题&#xff0c;那没办法重…

Android开发,日志级别

5个日志级别 Verbose (VERBOSE): 这是最低的日志级别&#xff0c;用于输出最为详尽的信息&#xff0c;包括开发和调试过程中的各种细节。在Log类中对应的方法是Log.v()。Debug (DEBUG): 此级别用于输出调试信息&#xff0c;帮助开发者理解程序运行流程或状态。通过Log.d()方法…

Mirror从入门到入神

Mirror从入门到成神 文章目录 Mirror从入门到成神简介NetworkClientRegisterPrefabConnect (string address)Disconnect ()activeactiveHost NetworkServerSpawn 简介 Mirror是一个unity网络同步框架&#xff0c;基于MonoBehaviour生命周期的回调的基础上进行数值的同步&#…

传输文件协议FTP与LFTP

目录 一.简介 二. FTP基础 主动模式&#xff08;Active Mode&#xff09;&#xff1a; 被动模式&#xff08;Passive Mode&#xff09;&#xff1a; 三. Vsftp 服务器简介 四. Vsftpd配置 1. 安装vsftpd&#xff08;ftp服务端&#xff09; 2.编辑配置文件 &#xff08;…

StNet: Local and Global Spatial-Temporal Modeling for Action Recognition 论文阅读

StNet: Local and Global Spatial-Temporal Modeling for Action Recognition 论文阅读 Abstract1 Introduction2 Related Work3 Proposed Approach4 Experiments5 Conclusion 文章信息&#xff1a; 原文链接&#xff1a;https://ojs.aaai.org/index.php/AAAI/article/view/4…

九、e2studio VS STM32CubeIDE之const修饰BSP函数的形参

目录 一、概述/目的 二、通过串口发送函数对比 2.1 stm32 hal库 VS renesas FSP 2.2 const修改函数形参的作用 2.2.1 值传递-副本 2.2.2 指针传递&#xff08;就近原则&#xff09; 2.2.2.1 const修饰&#xff1a;*P 2.2.2.2 const修饰&#xff1a;指针变量P 2.2.2.3 …

python的标准数据类型

四、标准数据类型 1、为什么编程语言中要有类型 类型有以下几个重要角色&#xff1a; 对机器而言&#xff0c;类型描述了内存中的电荷是怎么解释的。 对编译器或者解释器而言&#xff0c;类型可以协助确保上面那些电荷、字节在程序的运行中始终如一地被理解。 对程序员而言…

NARUTO 复现记录

1 环境配置 下载项目&#xff0c;一定要 git 下载全项目&#xff0c;下载完后要检查third_parities 里面的coslam和neural_slam_eval 文件全不全。 git clone --recursive https://github.com/oppo-us-research/NARUTO.git 环境配置 注意 bash scripts/installation/conda…

【JavaEE】HTTP 协议

文章目录 一、HTTP 协议1、HTTP 是什么2、理解 "应用层协议"3、理解 HTTP 协议的工作过程4、HTTP 协议格式5、HTTP 请求 (Request)5.1 认识 URL 6、 二、HTTPS1、HTTPS是什么2、"加密" 是什么3、HTTPS 的工作过程3.1 对称加密3.2 非对称加密3.3 证书3.4 完…

用suno创作歌曲音乐的8个技巧

导读 Suno Ai可以将文本转化为高度逼真的音乐和语音。 该系统包括多种音乐风格&#xff0c;如电影、RAP、翻唱等&#xff0c;并提供了多语言和不同性别的播音员选择。 用户可以使用命令来生成音频并进行个性化设置。 用suno.ai所生成的歌曲质量非常高&#xff0c;而且完美支…

C++——超简单登录项目

程序入口文件 #include <QtWidgets/QApplication> // 包含登录页面头文件 #include "DlgLogin.h"int main(int argc, char *argv[]) {QApplication a(argc, argv);// 程序入口// 调页面起来//DlgMain w;//w.show();// 换成登录页面DlgLogin w;w.show();return…

kafka学习笔记04(小滴课堂)

Kafka的producer生产者发送到Broker分区策略讲解 Kafka核心API模块-producer API讲解实战 代码&#xff1a; ProducerRecord介绍和key的作用 Kafka核心API模块-producerAPI回调函数实战 producer生产者发送指定分区实战 我们设置5个分区。 我们指定分区。 重新指定一个分区&am…

猫吃普通猫粮会秃头?细数生骨肉冻干喂养是否智商税

鉴于科学养猫的理念日益深入人心&#xff0c;生骨肉冻干喂养逐渐成为养猫人的首选。生骨肉冻干喂养不仅符合猫咪的自然习性&#xff0c;更提供了均衡全面的营养&#xff0c;有助于维护猫咪的口腔健康及消化系统功能。普通猫粮的营养远不如生骨肉冻干&#xff0c;长期喂食生骨肉…

110份财务常用excel模板(个税、采购、报销、预算),超实用!

如果你还在为报表头疼&#xff0c;那你一定不能错过这篇干货满满的分享&#xff01; 个税报表 个人所得税&#xff0c;听起来就头大&#xff1f;别担心&#xff0c;掌握这些技巧&#xff0c;轻松搞定&#xff01; - 记录员工收入&#xff0c;确保数据准确无误 - 计算应纳税…

Verilog代码bug:一种特殊的组合逻辑环

Verilog代码bug&#xff1a;一种特殊的组合逻辑环 组合逻辑环&#xff08;Combinational Loop&#xff09;是什么&#xff0c;别的文章已经写的很多了&#xff0c;本文就不赘述了&#xff0c;本文主要记录遇到过的一种特殊的逻辑环&#xff1b; 代码如下所示&#xff1a; mo…

【STM32】状态机实现定时器按键消抖,处理单击、双击、三击、长按事件

目录 一、简单介绍 二、模块与接线 三、cubemx配置 四、驱动编写 状态图 按键类型定义 参数初始化/复位 按键扫描 串口重定向 主函数 五、效果展示 六、驱动附录 key.c key.h 一、简单介绍 众所周知&#xff0c;普通的机械按键会产生抖动&#xff0c;可以采取硬件…

卡片笔记写作法 精读笔记 01

元数据 卡片笔记写作法&#xff1a;如何实现从阅读到写作 书名&#xff1a; 卡片笔记写作法&#xff1a;如何实现从阅读到写作作者&#xff1a; 申克阿伦斯简介&#xff1a; 卢曼的“盒中笔记”通常很简短&#xff0c;因为这些只是他庞大繁杂研究中的索引&#xff0c;等需要时&…

[沫忘录]MySQL 锁

[沫忘录]MySQL 锁 锁能够协调多线程或多进程并发访问某资源产生的数据冲突与错乱。而在数据库中&#xff0c;锁也是协调数据库访问的有效工具。 全局锁 能够锁住当前服务器所有数据库及其表。后续所有事务都只能进行读操作&#xff0c;而不能进行写操作或表属性更改。 典型…