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

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: rootpassword: 3fa4d180url: jdbc:mysql://localhost:3306/test01?useSSL=false&serverTimezone=UTCdriver-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 {@Autowiredprivate CronMapper cronMapper;@Overridepublic 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.hqwc.cn/news/700130.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Language2Pose: Natural Language Grounded Pose Forecasting # 论文阅读

URL https://arxiv.org/pdf/1907.01108 TD;DR 19 年 7 月 cmu 的文章&#xff0c;提出一种基于 natural language 生成 3D 动作序列的方法。通过一个简单的 CNN 模型应该就可以实现 Model & Method 首先定义一下任务&#xff1a; 输入&#xff1a;用户的自然语言&…

探索 Canva 的功能以及如何有效使用 Canva

『创意瞬间变现&#xff01;Canva AI Drawing 让你的文字描绘成艺术』 在数字设计和创意领域&#xff0c;Canva 是创新和用户友好性的灯塔。这个平台不仅简化了图形设计&#xff0c;还引入了 AI Drawing 等强大工具&#xff0c;使其成为专业人士和初学者的首选解决方案。让我们…

有 10000 个 if else 该如何优化?被问懵了!

这个问题可以看作是一道场景题&#xff0c;它考察一个程序员在面对复杂逻辑判断时的优化能力&#xff0c;也是在考察一个程序员临场发挥技术能力。 方案1&#xff1a;策略模式 使用策略模式确实可以提升代码的优雅性&#xff0c;但也会存在以下问题&#xff1a; 如果是大量的…

超链接a的应用

主要作用&#xff1a;从当前页面进行跳转 1.跳转到页面 <!-- 跳转到其他页面 --><a href"#" target"_blank">鸡你太美</a> <!-- 跳转到本地页面 --><a href"#" target"_self">鸡你太美</a> 2.跳转…

深度解刨性能测试工具Locust

&#x1f345; 视频学习&#xff1a;文末有免费的配套视频可观看 &#x1f345; 关注公众号【互联网杂货铺】&#xff0c;回复 1 &#xff0c;免费获取软件测试全套资料&#xff0c;资料在手&#xff0c;涨薪更快 Locust安装 …

(二刷)代码随想录第6天|242.有效的字母异位词、349.两个数组的交集

242.有效的字母异位词 242. 有效的字母异位词 - 力扣&#xff08;LeetCode&#xff09; 代码随想录 (programmercarl.com) 学透哈希表&#xff0c;数组使用有技巧&#xff01;Leetcode&#xff1a;242.有效的字母异位词_哔哩哔哩_bilibili 给定两个字符串 s 和 t &#xff…

【设计模式】JAVA Design Patterns——Abstract-document(抽象文档模式)

&#x1f50d; 目的 使用动态属性&#xff0c;并在保持类型安全的同时实现非类型化语言的灵活性。 &#x1f50d; 解释 抽象文档模式使您能够处理其他非静态属性。 此模式使用特征的概念来实现类型安全&#xff0c;并将不同类的属性分离为一组接口 真实世界例子 考虑由多个部…

数据结构 顺序表1

1. 何为顺序表&#xff1a; 顺序表是一种线性数据结构&#xff0c;是由一组地址连续的存储单元依次存储数据元素的结构&#xff0c;通常采用数组来实现。顺序表的特点是可以随机存取其中的任何一个元素&#xff0c;并且支持在任意位置上进行插入和删除操作。在顺序表中&#xf…

基于IDEA快速创建一个SpringMVC项目并且配置Tomcat

1&#xff0c;打开IDEA&#xff0c;新建Maven项目【使用web模板创建】 使用社区版的同学创建普通的maven项目&#xff0c;并配置项目的webapp&#xff0c;详情可参考 快速创建一个SpringMVC项目&#xff08;IDEA&#xff09; 2&#xff0c;在main目录下创建Java和resource目录…

二叉树基础oj练习【11道题】

二叉树基础oj练习 1.单值二叉树 题目&#xff1a; 单值二叉树 如果二叉树每个节点都具有相同的值&#xff0c;那么该二叉树就是单值二叉树。 只有给定的树是单值二叉树时&#xff0c;才返回 true&#xff1b;否则返回 false。 示例 1&#xff1a; 输入&#xff1a;[1,1,1…

汇昌联信:拼多多网店该如何开店?

拼多多网店的开设流程并不复杂&#xff0c;但需要细心和耐心去完成每一步。下面将详细阐述如何开设一家拼多多网店。 一、选择商品与定位 开设拼多多网店的第一步是确定你要销售的商品类型&#xff0c;这决定了你的目标客户群体和市场定位。你需要了解这些商品的市场需求、竞争…

什么是CCRC?做什么用的?

CCRC&#xff08;中国网络安全审查认证和市场监管大数据中心&#xff09;原名为中国网络安全审查技术与认证中心&#xff0c;也被称为中国信息安全认证中心&#xff08;ISCCC&#xff09;。 该中心是经中央机构编制委员会办公室批准成立的&#xff0c;其主要职责是依据国家法律…