分布式之任务调度学习一

news2024/11/29 2:54:48

1 任务调度

1.1 什么时候需要任务调度?

1.1.1 任务调度的背景

在业务系统中有很多这样的场景:
1、账单日或者还款日上午 10 点,给每个信用卡客户发送账单通知,还款通知。如何判断客户的账单日、还款日,完成通知的发送?
2、银行业务系统,夜间要完成跑批的一系列流程,清理数据,下载文件,解析文件,对账清算、切换结算日期等等。如何触发一系列流程的执行?
3、金融机构跟人民银行二代支付系统对接,人民银行要求低于 5W 的金额(小额支付)半个小时打一次包发送,以缓解并发压力。所以,银行的跨行转账分成了多个流程:录入、复核、发送。如何把半个小时以内的所有数据一次性发送。
类似于这种 1、基于准确的时刻或者固定的时间间隔触发的任务,或者 2、有批量数据需要处理,或者 3、要实现两个动作解耦的场景,我们都可以用任务调度来实现。

1.2 任务调度需求分析

任务调度的实现方式有很多,如果要实现我们的调度需求,我们对这个工具有什么样的基本要求呢?

1.2.1 基本需求

1)可以定义触发的规则,比如基于时刻、时间间隔、表达式。
2)可以定义需要执行的任务。比如执行一个脚本或者一段代码。任务和规则是分开的。
3)集中管理配置,持久配置。不用把规则写在代码里面,可以看到所有的任务配置,方便维护。重启之后任务可以再次调度——配置文件或者配置中心。
4)支持任务的串行执行,例如执行 A 任务后再执行 B 任务再执行 C 任务。
5)支持多个任务并发执行,互不干扰(例如 ScheduledThreadPoolExecutor)。
6)有自己的调度器,可以启动、中断、停止任务。
7)容易集成到 Spring。

1.3 任务调度工具对比

层次举例特点
操作系统Linux crontab Windows 计划任务只能执行简单脚本或者命令
数据库MySql,Oracle可以操作数据,不能执行Java代码
工具Kettle可以操作数据,执行脚本,没有集中配置
开发语言JDK Timer,ScheduleThreadPoolTimer:单线程,JDK1.5之后,ScheduleThreadPool(Cache,Fiexed,Single)没有集中配置,日程管理不够灵活
容器Spring Task,@Scheduled不支持集群
分布式框架XXL-JOB,Elatic-Job

@Scheduled 也是用 JUC 的 ScheduledExecutorService 实现的Scheduled(cron = “0 15 10 15 * ?”
1、 ScheduledAnnotationBeanPostProcessor 的 postProcessAfterInitialization 方法将@Scheduled 的方法包装为指定的 task
添加到 ScheduledTaskRegistrar 中
2、 ScheduledAnnotationBeanPostProcessor 会监听 Spring 的容器初始化事件,在 Spring 容器初始化完成后进行
TaskScheduler 实现类实例的查找,若发现有 SchedulingConfigurer 的实现类实例,则跳过 3
3、 查找 TaskScheduler 的实现类实例默认是通过类型查找,若有多个实现则会查找名字为"taskScheduler"的实现 Bean,
若没有找到则在 ScheduledTaskRegistrar 调度任务的时候会创建一个 newSingleThreadScheduledExecutor,将
TaskScheduler 的实现类实例设置到 ScheduledTaskRegistrar 属性中
4、 ScheduledTaskRegistrar 的 scheduleTasks 方法触发任务调度
5、 真正调度任务的类是 TaskScheduler 实现类中的 ScheduledExecutorService,由 J.U.C 提供

2 Quartz 基本介绍

官网:http://www.quartz-scheduler.org/
Quartz 的意思是石英,像石英表一样精确。
Quartz is a richly featured, open source job scheduling library that can be integrated within virtually any Java application -from the smallest stand-alone application to the largest e-commerce system. Quartz can be used to create simple or complex schedules for executing tens, hundreds, or even tens-of-thousands of jobs; jobs whose tasks are defined as standard Java components that may execute virtually anything you may program them to do. The Quartz Scheduler includes many enterprise-class features, such as support for JTA transactions and clustering.
Quatz 是一个特性丰富的,开源的任务调度库,它几乎可以嵌入所有的 Java 程序,从很小的独立应用程序到大型商业系统。Quartz 可以用来创建成百上千的简单的或者复杂的任务,这些任务可以用来执行任何程序可以做的事情。Quartz 拥有很多企业级的特性,包括支持 JTA 事务和集群。
Quartz 是一个老牌的任务调度系统,98 年构思,01 年发布到 sourceforge。现在更新比较慢,因为已经非常成熟了。
https://github.com/quartz-scheduler/quartz
Quartz 的目的就是让任务调度更加简单,开发人员只需要关注业务即可。他是用 Java 语言编写的(也有.NET 的版本)。Java 代码能做的任何事情,Quartz 都可以调度。
特点:
精确到毫秒级别的调度
可以独立运行,也可以集成到容器中
支持事务(JobStoreCMT )
支持集群
支持持久化

3 Quartz Java 编程

http://www.quartz-scheduler.org/documentation/quartz-2.3.0/
http://www.quartz-scheduler.org/documentation/quartz-2.3.0/quick-start.html

3.1 引入依赖

<dependency>
	<groupId>org.quartz-scheduler</groupId>
	<artifactId>quartz</artifactId>
	<version>2.3.0</version>
</dependency>

3.2 默认配置文件

org.quartz.core 包下,有一个默认的配置文件,quartz.properties。当我们没有定义一个同名的配置文件的时候,就会使用默认配置文件里面的配置。

org.quartz.scheduler.instanceName: DefaultQuartzScheduler
org.quartz.scheduler.rmi.export: false
org.quartz.scheduler.rmi.proxy: false
org.quartz.scheduler.wrapJobExecutionInUserTransaction: false
org.quartz.threadPool.class: org.quartz.simpl.SimpleThreadPool
org.quartz.threadPool.threadCount: 10
org.quartz.threadPool.threadPriority: 5
org.quartz.threadPool.threadsInheritContextClassLoaderOfInitializingThread: true
org.quartz.jobStore.misfireThreshold: 60000
org.quartz.jobStore.class: org.quartz.simpl.RAMJobStore

3.3 创建 Job

实现唯一的方法 execute(),方法中的代码就是任务执行的内容。此处仅输出字符串。

public class MyJob implements Job {
	public void execute(JobExecutionContext context) throws JobExecutionException {
		System.out.println("假发在哪里买的");
	}
}

在测试类 main()方法中,把 Job 进一步包装成 JobDetail。
必须要指定 JobName 和 groupName,两个合起来是唯一标识符。
可以携带 KV 的数据(JobDataMap),用于扩展属性,在运行的时候可以从 context获取到。

JobDetail jobDetail = JobBuilder.newJob(MyJob1.class)
.withIdentity("job1", "group1")
.usingJobData("gupao","2673")
.usingJobData("moon",5.21F)
.build();

3.4 创建 Trigger

在测试类 main()方法中,基于 SimpleTrigger 定义了一个每 2 秒钟运行一次、不断重复的 Trigger:

Trigger trigger = TriggerBuilder.newTrigger()
.withIdentity("trigger1", "group1")
.startNow()
.withSchedule(SimpleScheduleBuilder.simpleSchedule()
.withIntervalInSeconds(2)
.repeatForever())
.build();

3.5 创建 Scheduler

在测试类 main()方法中,通过 Factory 获取调度器的实例,把 JobDetail 和 Trigger绑定,注册到容器中。
Scheduler 先启动后启动无所谓,只要有 Trigger 到达触发条件,就会执行任务。

SchedulerFactory factory = new StdSchedulerFactory();
Scheduler scheduler = factory.getScheduler();
scheduler.scheduleJob(jobDetail, trigger);
scheduler.start();

注意这里,调度器一定是单例的。

3.6 体系结构总结

在这里插入图片描述

3.6.1 JobDetail

我们创建一个实现 Job 接口的类,使用 JobBuilder 包装成 JobDetail,它可以携带KV 的数据。

3.6.2 Trigger

定义任务的触发规律,Trigger,使用 TriggerBuilder 来构建。JobDetail 跟 Trigger
Trigger 接口在 Quartz 有 4 个继承的子接口
在这里插入图片描述
MutableTrigger 和 CoreTrigger 最终也是用到以上四个类的实现类
在这里插入图片描述
SimpleTrigger
SimpleTrigger 可以定义固定时刻或者固定时间间隔的调度规则(精确到毫秒)。
例如:每天 9 点钟运行;每隔 30 分钟运行一次
CalendarIntervalTrigger
CalendarIntervalTrigger 可以定义更多时间单位的调度需求,精确到秒。
好处是不需要去计算时间间隔,比如 1 个小时等于多少毫秒。
例如每年、每个月、每周、每天、每小时、每分钟、每秒。
每年的月数和每个月的天数不是固定的,这种情况也适用。
DailyTimeIntervalTrigger
每天的某个时间段内,以一定的时间间隔执行任务。
例如:每天早上 9 点到晚上 9 点,每隔半个小时执行一次,并且只在周一到周六执行。
CronTrigger
CronTirgger 可以定义基于 Cron 表达式的调度规则,是最常用的触发器类型。
Cron 表达式
在这里插入图片描述
星号():可用在所有字段中,表示对应时间域的每一个时刻,例如,在分钟字段时,表示“每分钟”;
问号(?):该字符只在日期和星期字段中使用,它通常指定为“无意义的值”,相当于点位符;
减号(-):表达一个范围,如在小时字段中使用“10-12”,则表示从 10 到 12 点,即 10,11,12;
逗号(,):表达一个列表值,如在星期字段中使用“MON,WED,FRI”,则表示星期一,星期三和星期五;
斜杠(/):x/y 表达一个等步长序列,x 为起始值,y 为增量步长值。如在分钟字段中使用 0/15,则表示为 0,15,30 和
45 秒,而 5/15 在分钟字段中表示 5,20,35,50,你也可以使用
/y,它等同于 0/y;
L:该字符只在日期和星期字段中使用,代表“Last”的意思,但它在两个字段中意思不同。L 在日期字段中,表示这个月份的最后一天,如一月的 31 号,非闰年二月的 28 号;如果 L 用在星期中,则表示星期六,等同于 7。但是,如果 L 出现在星期字段里,而且在前面有一个数值 X,则表示“这个月的最后 X 天”,例如,6L 表示该月的最后星期五;
W:该字符只能出现在日期字段里,是对前导日期的修饰,表示离该日期最近的工作日。例如 15W 表示离该月 15号最近的工作日,如果该月 15 号是星期六,则匹配 14 号星期五;如果 15 日是星期日,则匹配 16 号星期一;如果 15号是星期二,那结果就是 15 号星期二。但必须注意关联的匹配日期不能够跨月,如你指定 1W,如果 1 号是星期六,结果匹配的是 3 号星期一,而非上个月最后的那天。W 字符串只能指定单一日期,而不能指定日期范围;
LW 组合:在日期字段可以组合使用 LW,它的意思是当月的最后一个工作日;
井号(#):该字符只能在星期字段中使用,表示当月某个工作日。如 6#3 表示当月的第三个星期五(6 表示星期五,
#3 表示当前的第三个),而 4#5 表示当月的第五个星期三,假设当月没有第五个星期三,忽略不触发;
C:该字符只在日期和星期字段中使用,代表“Calendar”的意思。它的意思是计划所关联的日期,如果日期没有被关联,则相当于日历中所有日期。例如 5C 在日期字段中就相当于日历 5 日以后的第一天。1C 在星期字段中相当于星期日后的第一天。
Cron 表达式对特殊字符的大小写不敏感,对代表星期的缩写英文大小写也不敏感。

基于 Calendar 的排除规则
如果要在触发器的基础上,排除一些时间区间不执行任务,就要用到 Quartz 的Calendar 类(注意不是 JDK 的 Calendar)。可以按年、月、周、日、特定日期、Cron表达式排除。
在这里插入图片描述
调用 Trigger 的 modifiedByCalendar()添加到触发器中,并且调用调度器的addCalendar()方法注册排除规则。

Calendar名称用法
BaseCalendar为高级的 Calendar 实现了基本的功能,实现了 org.quartz.Calendar 接口
AnnualCalendar排除年中一天或多天
CronCalendar日历的这种实现排除了由给定的 CronExpression 表达的时间集合。 例如,您可以使用此日历使用表达式“* * 0-7,18-23?* *”每天排除所有营业时间(上午 8 点至下午 5 点)。 如果 CronTrigger 具有给定的 cron 表达式并且与具有相同表达式的 CronCalendar 相关联,则日历将排除触发器包含的所有时间,并且它们将彼此抵消。
DailyCalendar您可以使用此日历来排除营业时间(上午 8 点 - 5 点)每天。 每个DailyCalendar 仅允许指定单个时间范围,并且该时间范围可能不会跨越每日边界(即,您不能指定从上午 8 点至凌晨 5 点的时间范围)。 如果属性 invertTimeRange 为 false(默认),则时间范围定义触发器不允许触发的时间范围。 如果 invertTimeRange 为 true,则时间范围被反转 - 也就是排除在定义的时间范围之外的所有时间。
HolidayCalendar特别的用于从 Trigger 中排除节假日
MonthlyCalendar排除月份中的指定数天,例如,可用于排除每月的最后一天
WeeklyCalendar排除星期中的任意周几,例如,可用于排除周末,默认周六和周日
3.6.3 Scheduler

调度器,是 Quartz 的指挥官,由 StdSchedulerFactory 产生。它是单例的。
并且是 Quartz 中最重要的 API,默认是实现类是 StdScheduler,里面包含了一个
QuartzScheduler。QuartzScheduler 里面又包含了一个 QuartzSchedulerThread
在这里插入图片描述
Scheduler 中的方法主要分为三大类:
1)操作调度器本身,例如调度器的启动 start()、调度器的关闭 shutdown()。
2)操作 Trigger,例如 pauseTriggers()、resumeTrigger()。
3)操作 Job,例如 scheduleJob()、unscheduleJob()、rescheduleJob()
这些方法非常重要,可以实现任务的动态调度。

3.6.4 Listener

我们有这么一种需求,在每个任务运行结束之后发送通知给运维管理员。那是不是要在每个任务的最后添加一行代码呢?这种方式对原来的代码造成了入侵,不利于维护。如果代码不是写在任务代码的最后一行,怎么知道任务执行完了呢?或者说,怎么监测到任务的生命周期呢?
观察者模式:定义对象间一种一对多的依赖关系,使得每当一个对象改变状态,则所有依赖它的对象都会得到通知并自动更新。
Quartz 中提供了三种 Listener,监听 Scheduler 的,监听 Trigger 的,监听 Job 的。
只需要创建类实现相应的接口,并在 Scheduler 上注册 Listener,便可实现对核心对象的监听。
JobListener
四个方法:

方法作用或执行实际
getName()返回 JobListener 的名称
jobToBeExecuted()Scheduler 在 JobDetail 将要被执行时调用这个方法
jobExecutionVetoed()Scheduler 在 JobDetail 即将被执行,但又被 TriggerListener 否决了时调用这个
方法
jobWasExecuted()Scheduler 在 JobDetail 被执行之后调用这个方法

工具类:ListenerManager,用于添加、获取、移除监听器
工具类:Matcher,主要是基于 groupName

在这里插入图片描述
TriggerListener

方法作用或执行实际
getName()返回监听器的名称
jtriggerFired()Trigger 被触发,Job 上的 execute() 方法将要被执行时,Scheduler 就调用这个
方法
vetoJobExecution()在 Trigger 触 发 后 , Job 将 要 被 执 行 时 由 Scheduler 调 用 这 个 方 法 。TriggerListener 给了一个选择去否决 Job 的执行。假如这个方法返回 true,这个 Job 将不会为此次 Trigger 触发而得到执行
triggerMisfired()Trigger 错过触发时调用
triggerComplete()Trigger 被触发并且完成了 Job 的执行时,Scheduler 调用这个方法
3.6.5 JobStore

问题:最多可以运行多少个任务(磁盘、内存、线程数)
Jobstore 用来存储任务和触发器相关的信息,例如所有任务的名称、数量、状态等等。Quartz 中有两种存储任务的方式,一种在在内存,一种是在数据库。
RAMJobStore
Quartz 默认的 JobStore 是 RAMJobstore,也就是把任务和触发器信息运行的信息存储在内存中,用到了 HashMap、TreeSet、HashSet 等等数据结构。
如果程序崩溃或重启,所有存储在内存中的数据都会丢失。所以我们需要把这些数据持久化到磁盘。
JDBCJobStore
JDBCJobStore 可以通过 JDBC 接口,将任务运行数据保存在数据库中。
在这里插入图片描述
JDBC 的实现方式有两种,JobStoreSupport 类的两个子类:
JobStoreTX:在独立的程序中使用,自己管理事务,不参与外部事务。
JobStoreCMT:(Container Managed Transactions (CMT),如果需要容器管理事务时,使用它
使用 JDBCJobSotre 时,需要配置数据库信息:

org.quartz.jobStore.class:org.quartz.impl.jdbcjobstore.JobStoreTX
org.quartz.jobStore.driverDelegateClass:org.quartz.impl.jdbcjobstore.StdJDBCDelegate
# 使用 quartz.properties,不使用默认配置
org.quartz.jobStore.useProperties:true
#数据库中 quartz 表的表名前缀
org.quartz.jobStore.tablePrefix:QRTZ_
org.quartz.jobStore.dataSource:myDS
#配置数据源
org.quartz.dataSource.myDS.driver:com.mysql.jdbc.Driver
org.quartz.dataSource.myDS.URL:jdbc:mysql://localhost:3306/gupao?useUnicode=true&characterEncoding=utf8
org.quartz.dataSource.myDS.user:root
org.quartz.dataSource.myDS.password:123456
org.quartz.dataSource.myDS.validationQuery=select 

在官网的 Downloads 链接中,提供了 11 张表的建表语句:
quartz-2.2.3-distribution\quartz-2.2.3\docs\dbTables
2.3 的版本在这个路径下:src\org\quartz\impl\jdbcjobstore
表名与作用:
在这里插入图片描述

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

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

相关文章

类加载机制之双亲委派模型、作用、源码、SPI打破双亲委派模型

双亲委派模型 双亲委派工作机制双亲委派的作用双亲委派的实现源码SPI打破双亲委派 应用程序是由三种类加载器相互配合&#xff0c;从而实现类加载&#xff0c;除此之外还可以加入自己定义的类的加载器。 类加载器之间的层次关系&#xff0c;称为双亲委派模型&#xff08;Parent…

飞腾Ubantu22.04.3安装OpenNebula测试

目前只有one服务能够启动成功&#xff0c;sunstone-server nodejs 构建存在问题。 1.概述 因OpenneBula官方镜像源只有AMD架构的镜像包不存在ARM的镜像包&#xff0c;借此用源码编译进行测试。 2.官网github地址 下载解压存放在服务器上&#xff1a; https://github.com/O…

轻量检测模型PP-PicoDet解析

Paper&#xff1a;PP-PicoDet: A Better Real-Time Object Detector on Mobile Devices official implementation&#xff1a;https://github.com/PaddlePaddle/PaddleDetection/tree/release/2.7/configs/picodet Backbone 作者通过实验发现&#xff0c;ShuffleNetV2在移动…

2024第一篇: 架构师成神之路总结,你值得拥有

大家好&#xff0c;我是冰河~~ 很多小伙伴问我进大厂到底需要怎样的技术能力&#xff0c;经过几天的思考和总结&#xff0c;终于梳理出一份相对比较完整的技能清单&#xff0c;小伙伴们可以对照清单提前准备相关的技能&#xff0c;在平时的工作中注意积累和总结。 只要在平时…

Apache Commons BCEL与Java字节码操作

第1章&#xff1a;Apache Commons BCEL简介 大家好&#xff0c;我是小黑&#xff0c;咱们今天来聊聊Apache Commons BCEL&#xff08;Byte Code Engineering Library&#xff09;。你可能会问&#xff0c;BCEL是什么鬼&#xff1f;别急&#xff0c;小黑这就给你娓娓道来。BCEL…

Hadoop入门学习笔记——八、数据分析综合案例

视频课程地址&#xff1a;https://www.bilibili.com/video/BV1WY4y197g7 课程资料链接&#xff1a;https://pan.baidu.com/s/15KpnWeKpvExpKmOC8xjmtQ?pwd5ay8 Hadoop入门学习笔记&#xff08;汇总&#xff09; 目录 八、数据分析综合案例8.1. 需求分析8.1.1. 背景介绍8.1.2…

基于Python实现二手房整体情况可视化分析+房价预测【500010099】

项目描述 通过房天下兰州二手房信息&#xff0c;对数据进行进一步清洗处理&#xff0c;分析各维度的数据&#xff0c;筛选对房价有显著影响的特征变量&#xff0c;探索兰州二手房整体情况、价格情况和价格的影响因素&#xff0c;建立房价预测模型。 提出问题 探究单价、数量…

SVN服务端的下载、安装

地址 &#xff1a; Apache Subversion Binary Packages 下载 点击 VisualSVN 安装 都是点击 next 点击next &#xff0c;即可安装成功

代码随想录算法训练营第五十七天|647. 回文子串、516.最长回文子序列、动态规划总结篇

代码随想录 (programmercarl.com) 647. 回文子串 1.dp数组及下标含义 我们在判断字符串S是否是回文&#xff0c;那么如果我们知道 s[1]&#xff0c;s[2]&#xff0c;s[3] 这个子串是回文的&#xff0c;那么只需要比较 s[0]和s[4]这两个元素是否相同&#xff0c;如果相同的话&…

矩阵式键盘按键音

#include<reg51.h> //包含51单片机寄存器定义的头文件 sbit soundP3^7; //将sound位定义为P3.7 /************************************************************** 函数功能&#xff1a;蜂鸣器发声延时约120ms *************************************************…

Linux 内核调试

文章目录 一、方法论 一、方法论 qemu 虚拟机 Linux内核学习 Linux 内核调试 一&#xff1a;概述 Linux 内核调试 二&#xff1a;ubuntu20.04安装qemu Linux 内核调试 三&#xff1a;《QEMU ARM guest support》翻译 Linux 内核调试 四&#xff1a;qemu-system-arm功能选项整…

大数据毕业设计:租房推荐系统 python 租房大数据 爬虫+可视化大屏 计算机毕业设计(附源码+文档)✅

毕业设计&#xff1a;2023-2024年计算机专业毕业设计选题汇总&#xff08;建议收藏&#xff09; 毕业设计&#xff1a;2023-2024年最新最全计算机专业毕设选题推荐汇总 &#x1f345;感兴趣的可以先收藏起来&#xff0c;点赞、关注不迷路&#xff0c;大家在毕设选题&#xff…

年度征文|回顾2023我的CSDN

一年转眼而逝&#xff0c;回顾这一年在csdn的创作&#xff0c;学习&#xff0c;记录历程。回顾过去&#xff0c;才能展望未来&#xff0c;首先看图说话。 今年在csdn的访问量已由年初的2万到年末的50w。粉丝有年初的300个左右&#xff0c;增加到4000个左右。我年初的目标是粉丝…

Dockerfile语法和简单镜像构建

Dockerfile是一个用于定义Docker镜像的文本文件&#xff0c;包含了一系列的指令和参数&#xff0c;用于指示Docker在构建镜像时应该执行哪些操作&#xff0c;例如基于哪个基础镜像、复制哪些文件到镜像中、运行哪些命令等。 Dockerfile文件的内容主要有几个部分组成&#xff0c…

算法——BFS解决FloodFill算法

什么是FloodFill算法 中文&#xff1a;洪水灌溉。假设这一块4*4的方格是一块土地&#xff0c;有凸起的地方&#xff0c;也有凹陷的地方&#xff08;凹陷的地方用负数表示&#xff09;。此时下大雨发洪水&#xff0c;会把凹陷的地方填满。绿色圈起来的属于一块区域&#xff08;…

caj转换成pdf有哪些方法?

caj转换成pdf有哪些方法&#xff1f;PDF是一个被广泛支持的文件格式&#xff0c;这种格式基本上在所有的操作系统和设备上都是支持使用的&#xff0c;也能够将PDF文件打开和查看的&#xff0c;相比于caj文件&#xff0c;它就只能通过一下特定的软件或者是插件才能够将caj打开或…

3.2.1CURRENT 、3.2.2-SNAPSHOT、3.1.7GA 这三者的springboot版本之前有什么区别

在Spring Boot中&#xff0c;版本号通常遵循主要版本.次要版本.修补版本的格式&#xff0c;有时后面会跟着一个额外的标签来表示版本的特殊性质。根据您提供的版本号 “3.2.1CURRENT”、“3.2.2-SNAPSHOT” 和 “3.1.7GA”&#xff0c;我们可以解释这些版本的含义和它们之间的区…

用贪心算法编程求解任务安排问题

题目&#xff1a;用贪心算法编程求解以下任务安排问题 一个单位时间任务是恰好需要一个单位时间完成的任务。给定一个单位时间任务的有限集S。关于S的一个时间表用于描述S中单位时间任务的执行次序。时间表中第1个任务从时间0 开始执行直至时间1 结束&#xff0c;第2 个任务从时…

react-router-dom5升级到6

前言 升级前版本为5.1.2 下载与运行 下载 npm install react-router-dom6运行 运行发现报错: 将node_modules删除&#xff0c;重新执行npm i即可 运行发现如下报错 这是因为之前有引用react-router-dom.min&#xff0c;v6中取消了该文件&#xff0c;所以未找到文件导致报错。…

鸿蒙4.0开发实战(ArkTS)-闹钟制作

闹钟功能要求 展示指针表盘或数字时间。添加、修改和删除闹钟。展示闹钟列表&#xff0c;并可打开和关闭单个闹钟。闹钟到设定的时间后弹出提醒。将闹钟的定时数据保存到轻量级数据库。 闹钟主界面 闹钟界面包括当前时间、闹钟列表、添加闹钟子组件&#xff0c;具体包括以下…