Quartz任务调度框架介绍和使用
一、Quartz介绍
Quartz [kwɔːts] 是OpenSymphony开源组织在Job scheduling领域又一个开源项目,完全由Java开发,可以用来执行定时任务,类似于java.util.Timer。但是相较于Timer, Quartz增加了很多功能:
1.持久性作业 - 就是保持调度定时的状态;
2.作业管理 - 对调度作业进行有效的管理;
Quartz是一个强大任务调度框架,可以用来干嘛?
简单来说就是实现“计划(或定时)任务”的系统,例如:
订单下单后未付款,15分钟后自动撤消订单,并自动解锁锁定的商品;
一个OA系统需要在每周五9点自动生成数据报表;
比如vip的每月自动续费功能;
或者想每月10号自动还款;
又或者每周给暗恋的女生定时发送邮件等等。
Java 语言实现定时任务的几种方式
java.util.Timer:一个 JDK 中自带的处理简单定时任务的工具。
java.util.concurrent.ScheduledExecutorService:JDK 中的定时任务接口,可以将定时任务与线程池功能结合使用。
org.springframework.scheduling.annotation.Scheduled:Spring 框架中基于注解来实现定时任务处理。
Quartz:一个完全由 Java 语言编写的,支持分布式调度任务的开源框架。
二、Quartz的核心概念
三大核心类 JObDetail(作业类),Trigger(触发器),Scheduler(调度器)。Trigger指定JObDetail什么时候发布任务。
1,任务job
job就是你想实现的任务类,每一个job必须实现org.quartz.job接口,且只需实现接口定义的execute()方法。
Job:工作任务调度的接口,任务类需要实现该接口,该接口中定义execute方法,类似jdk提供的TimeTask类的run方法,在里面编写任务执行的业务逻辑。
Job:实例在Quartz中的生命周期,每次调度器执行job时它在调用execute方法前,会创建一个新的job实例,当调用完成后,关联的job对象实例会被是释放,释放的实例会被垃圾回收机制回收。
2,触发器Trigger
Trigger 为你执行任务的触发器,比如你想每天定时1点发送邮件,Trigger将会设置1点执行该任务。
Trigger主要包含两种:SimpleTrigger和CronTriggerr。
3,调度器Scheduler
Scheduler是任务的调度器,会将任务job和触发器TRigger结合,负责基于Trigger设定的时间执行job。
三、Quartz的几个常用API
Scheduler :用于与调度程序交互的主程序接口。
Job :预先定义的希望在未来时间被调度程序执行的任务类,自定义。
JobDetall :使用JobDetail来定义定时任务的实例,JobDetail实例是通过JobBuilder类创建。
JobDataMap :可包含数据对象,在job实例执行的是好,可使用包含的数据;JobDataMap是java Map接口的实现,增加了一些存取基本类型方法。
Trgger触发器 :Trigger对象是用于触发执行Job的,当调度一个Job时,我们实例一个触发器然后调整它的属性来满足Job执行的条件,表明任务在什么时候执行。定义了一个已经被安排的任务将在什么时候执行的时间条件,比如每秒执行一次。
JobBuilder :用于声明一个任务实例,也可以定义关于该任务的详情比如:任务名,组名等,这个声明的实例将作为一个实例执行的任务。
TriggerBuilder :触发器创建器,用于创建触发器trigger实例。
JobListener,TriggerListener,SchedulerListener监听器,用于对组件的监听。
四、Quartz的简单使用
运行程序,可以看到程序每隔1s会打印出内容,且在12s后程序结束。
创建项目并加入依赖,参考:【普通的IDEA maven java项目demo(hello word)-1.8】待更新CSDN链接
<dependency>
<groupId>org.quartz-scheduler</groupId>
<artifactId>quartz</artifactId>
<version>2.3.2</version>
</dependency>
新建一个能够打印任意内容的Job:
import org.quartz.Job;
import org.quartz.JobExecutionContext;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Random;
public class PrintWordsJob implements Job {
@Override
public void execute(JobExecutionContext jobExecutionContext) {
String printTime = new SimpleDateFormat("yy-MM-dd HH-mm-ss:SSS").format(new Date());
System.out.println("PrintWordsJob start at:" + printTime + ", prints: Hello Job-" + new Random().nextInt(100));
}
}
创建Schedule,执行任务:
import org.quartz.*;
import org.quartz.impl.StdSchedulerFactory;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.concurrent.TimeUnit;
public class Demo {
public static void main(String[] args) throws SchedulerException, InterruptedException {
// 1、创建JobDetail实例,并与PrintWordsJob类绑定(Job执行内容)
JobDetail jobDetail = JobBuilder.newJob(PrintWordsJob.class)
.withIdentity("job1", "group1").build();
// 2、构建Trigger实例,每隔1s执行一次
Trigger trigger = TriggerBuilder.newTrigger().withIdentity("trigger1", "triggerGroup1")
.startNow()// 立即生效
.withSchedule(SimpleScheduleBuilder.simpleSchedule()
.withIntervalInSeconds(1)// 每隔1s执行一次
.repeatForever()).build();// 一直执行
// 3、创建调度器Scheduler并执行
Scheduler scheduler = new StdSchedulerFactory().getScheduler();
scheduler.scheduleJob(jobDetail, trigger);
System.out.println("--------scheduler start ! ------------");
System.out.println("at:" + new SimpleDateFormat("yy-MM-dd HH-mm-ss:SSS").format(new Date()) + ", prints: Hello scheduler");
scheduler.start();
// 睡眠12秒
TimeUnit.MILLISECONDS.sleep(12000);
scheduler.shutdown();
System.out.println("at:" + new SimpleDateFormat("yy-MM-dd HH-mm-ss:SSS").format(new Date()) + ", prints: Hello scheduler");
System.out.println("--------scheduler shutdown ! ------------");
}
}
运行结果(注意看时间):
--------scheduler start ! ------------
at:23-08-22 00-05-01:123, prints: Hello scheduler
PrintWordsJob start at:23-08-22 00-05-01:129, prints: Hello Job-69
PrintWordsJob start at:23-08-22 00-05-02:052, prints: Hello Job-68
PrintWordsJob start at:23-08-22 00-05-03:057, prints: Hello Job-93
PrintWordsJob start at:23-08-22 00-05-04:061, prints: Hello Job-32
PrintWordsJob start at:23-08-22 00-05-05:057, prints: Hello Job-14
PrintWordsJob start at:23-08-22 00-05-06:051, prints: Hello Job-55
PrintWordsJob start at:23-08-22 00-05-07:058, prints: Hello Job-30
PrintWordsJob start at:23-08-22 00-05-08:048, prints: Hello Job-82
PrintWordsJob start at:23-08-22 00-05-09:058, prints: Hello Job-28
PrintWordsJob start at:23-08-22 00-05-10:059, prints: Hello Job-97
PrintWordsJob start at:23-08-22 00-05-11:053, prints: Hello Job-88
PrintWordsJob start at:23-08-22 00-05-12:048, prints: Hello Job-18
PrintWordsJob start at:23-08-22 00-05-13:057, prints: Hello Job-93
at:23-08-22 00-05-13:135, prints: Hello scheduler
--------scheduler shutdown ! ------------
五、Quartz核心详解
1.Job和JobDetail
Job是Quartz中的一个接口,接口下只有execute方法,在这个方法中编写业务逻辑。
JobDetail用来绑定Job,为Job实例提供许多属性:name、group、jobClass、jobDataMap
JobDetail绑定指定的Job,每次Scheduler调度执行一个Job的时候,首先会拿到对应的Job,然后创建该Job实例,再去执行Job中的execute()的内容,任务执行结束后,关联的Job对象实例会被释放,且会被JVM GC清除。
为什么设计成JobDetail + Job,不直接使用Job?
JobDetail定义的是任务数据,而真正的执行逻辑是在Job中。 这是因为任务是有可能并发执行,如果Scheduler直接使用Job,就会存在对同一个Job实例并发访问的问题。而JobDetail & Job 方式,Sheduler每次执行,都会根据JobDetail创建一个新的Job实例,这样就可以规避并发访问的问题。
2.Trigger、SimpleTrigger、CronTrigger
Trigger
Trigger是Quartz的触发器,会去通知Scheduler何时去执行对应Job。
new Trigger().startAt():表示触发器首次被触发的时间;
new Trigger().endAt():表示触发器结束触发的时间;
SimpleTrigger
SimpleTrigger可以实现在一个指定时间段内执行一次作业任务或一个时间段内多次执行作业任务。
将下述代码替换上述【Quartz的简单使用】代码的 // 2、构建Trigger实例,每隔1s执行一次 内容
程序运行5s后开始执行Job,执行Job 5s后,再延时2s结束程序:
// 2、构建Trigger实例,每隔1s执行一次
Date startDate = new Date();
startDate.setTime(startDate.getTime() + 5000);
Date endDate = new Date();
endDate.setTime(startDate.getTime() + 5000);
Trigger trigger = TriggerBuilder.newTrigger().withIdentity("trigger1", "triggerGroup1")
.usingJobData("trigger1", "这是jobDetail1的trigger")
.startNow()//立即生效
.startAt(startDate)
.endAt(endDate)
.withSchedule(SimpleScheduleBuilder.simpleSchedule()
.withIntervalInSeconds(1)// 每隔1s执行一次
.repeatForever()).build();// 一直执行
运行结果(注意看时间):
--------scheduler start ! ------------
at:23-08-22 00-08-34:658, prints: Hello scheduler
PrintWordsJob start at:23-08-22 00-08-39:573, prints: Hello Job-81
PrintWordsJob start at:23-08-22 00-08-40:553, prints: Hello Job-63
PrintWordsJob start at:23-08-22 00-08-41:560, prints: Hello Job-87
PrintWordsJob start at:23-08-22 00-08-42:562, prints: Hello Job-25
PrintWordsJob start at:23-08-22 00-08-43:554, prints: Hello Job-65
at:23-08-22 00-08-46:666, prints: Hello scheduler
--------scheduler shutdown ! ------------
CronTrigger
CronTrigger功能非常强大,是基于日历的作业调度,而SimpleTrigger是精准指定间隔,所以相比SimpleTrigger,CroTrigger更加常用。CroTrigger是基于Cron表达式的,了解Cron表达式可参考:【cron表达式 详解】cron表达式 详解_linux cron表达式_西晋的no1的博客-CSDN博客
在线生成corn表达式: 在线Cron表达式生成器
将下述代码替换上述【Quartz的简单使用】代码的 // 2、构建Trigger实例,每隔1s执行一次 内容
从0秒开始,每5秒执行一次定时任务
// 2.触发器
CronTrigger trigger = TriggerBuilder.newTrigger().withIdentity("trigger", "group").startNow()//立刻执行
.usingJobData("trigger1", "这是jobDetail1的trigger")
.withSchedule(CronScheduleBuilder.cronSchedule("0/5 * * * * ? *"))//表示每次0秒时候执行。
.build();
运行结果(注意看时间):
--------scheduler start ! ------------
at:23-08-22 00-21-35:870, prints: Hello scheduler
PrintWordsJob start at:23-08-22 00-21-35:877, prints: Hello Job-39
PrintWordsJob start at:23-08-22 00-21-40:001, prints: Hello Job-68
PrintWordsJob start at:23-08-22 00-21-45:002, prints: Hello Job-8
at:23-08-22 00-21-47:873, prints: Hello scheduler
--------scheduler shutdown ! ------------
六、JobListener
创建MyJobListener实现JobListener接口
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;
import org.quartz.JobListener;
public class MyJobListener implements JobListener {
public String getName() {
return this.getClass().getSimpleName();
}
//Scheduler在jobDetail将要被执行时调用这个方法(执行前)
public void jobToBeExecuted(JobExecutionContext context) {
String jobName = context.getJobDetail().getKey().getName();
System.out.println("我的job名1:" + jobName);
}
//Scheduler在jobDetail即将被执行,但又被TriggerListermer 否定时会调用该方法
public void jobExecutionVetoed(JobExecutionContext context) {
String jobName = context.getJobDetail().getKey().getName();
System.out.println("我的job名2:" + jobName);
}
//Scheduler在jobDetail即将被执行之后调用这个方法。(执行后)
public void jobWasExecuted(JobExecutionContext context, JobExecutionException jobException) {
String jobName = context.getJobDetail().getKey().getName();
System.out.println("我的job名3:" + jobName);
}
}
在scheduler创建后加入监听即可生效
scheduler.getListenerManager().addJobListener(new MyJobListener());
将下述蓝色代码放于上述【Quartz的简单使用】对应位置
// 3、创建调度器Scheduler并执行
Scheduler scheduler = new StdSchedulerFactory().getScheduler();
scheduler.getListenerManager().addJobListener(new MyJobListener());
scheduler.scheduleJob(jobDetail, trigger);
运行结果(注意看时间):
--------scheduler start ! ------------
at:23-08-22 00-28-12:174, prints: Hello scheduler
我的job名1:job1
PrintWordsJob start at:23-08-22 00-28-12:179, prints: Hello Job-7
我的job名3:job1
我的job名1:job1
PrintWordsJob start at:23-08-22 00-28-13:112, prints: Hello Job-39
我的job名3:job1
我的job名1:job1
…
…
…
PrintWordsJob start at:23-08-22 00-28-23:111, prints: Hello Job-0
我的job名3:job1
我的job名1:job1
PrintWordsJob start at:23-08-22 00-28-24:115, prints: Hello Job-12
我的job名3:job1
at:23-08-22 00-28-24:179, prints: Hello scheduler
--------scheduler shutdown ! ------------
七、TriggerListener
任务调度过程中,与触发器Trigger相关的事件包括:触发器触发,触发器未正常触发,触发器完成等。
import org.quartz.JobExecutionContext;
import org.quartz.Trigger;
import org.quartz.TriggerListener;
public class MyTriggerListener implements TriggerListener {
//用于获取触发器的名称
public String getName() {//获取默认类名
return this.getClass().getSimpleName();
}
//当与监听器相关联的trigger被触发,job上的execute()方法将被执行时,Scheduler就调用该方法
public void triggerFired(Trigger trigger, JobExecutionContext context) {
System.out.println("triggerFired");
}
//在Trigger触发后,job将要被执行时由Scheduler调用这个方法。
//TriggerListener给一个选择去否决job的执行。如方法返回true,job此次将不会为trigger触发执行。false,放行。
public boolean vetoJobExecution(Trigger trigger, JobExecutionContext context) {
System.out.println("vetoJobExecution");
return false;
}
//Scheduler 调用这个方法是在trigger错过时触发。
public void triggerMisfired(Trigger trigger) {
System.out.println("triggerMisfired");
}
//triggerComplete:trigger被触发并且完成了job的执行时,Scheduler调用这个方法。
public void triggerComplete(Trigger trigger, JobExecutionContext context,
Trigger.CompletedExecutionInstruction triggerInstructionCode) {
System.out.println("triggerComplete");
}
}
将下述蓝色代码放于上述【Quartz的简单使用】对应位置
// 3、创建调度器Scheduler并执行
Scheduler scheduler = new StdSchedulerFactory().getScheduler();
scheduler.getListenerManager().addTriggerListener(new MyTriggerListener());
scheduler.scheduleJob(jobDetail, trigger);
运行结果(注意看时间):
--------scheduler start ! ------------
at:23-08-22 00-34-56:592, prints: Hello scheduler
triggerFired
vetoJobExecution
PrintWordsJob start at:23-08-22 00-34-56:601, prints: Hello Job-69
triggerComplete
triggerFired
vetoJobExecution
…
…
…
PrintWordsJob start at:23-08-22 00-35-07:530, prints: Hello Job-13
triggerComplete
triggerFired
vetoJobExecution
PrintWordsJob start at:23-08-22 00-35-08:535, prints: Hello Job-21
triggerComplete
at:23-08-22 00-35-08:597, prints: Hello scheduler
--------scheduler shutdown ! ------------
八、SchedulerListener
SchedulerListener会在scheduler的生命周期中关键事件发生时被调用,与Scheduler有关事件;增加或者删除一个 job/trigger,关闭scheduler等。
import org.quartz.*;
public class MySchedulerListener implements SchedulerListener {
//用于部署JobDetail 的时候调用
public void jobScheduled(Trigger trigger) {
String name = trigger.getKey().getName();
System.out.println("获取触发器名称:" + name);
}
//卸载JobDetail 的时候调用
public void jobUnscheduled(TriggerKey triggerKey) {
System.out.println(triggerKey.getName());
}
//当trigger来到再也不会触发的时候调用这个方法
public void triggerFinalized(Trigger trigger) {
String name = trigger.getKey().getName();
System.out.println("获取触发器名称:" + name);
}
//当trigger 被暂停时候调用
public void triggerPaused(TriggerKey triggerKey) {
System.out.println("被暂停1");
}
//当trigger组 被暂停时候调用
public void triggersPaused(String triggerGroup) {
System.out.println("被暂停2");
}
///当trigger从 被暂停 到恢复 时候 调用
public void triggerResumed(TriggerKey triggerKey) {
System.out.println("恢复");
}
///当trigger组从 被暂停 到恢复 时候 调用
public void triggersResumed(String triggerGroup) {
System.out.println("恢复");
}
//添加工作任务调用
public void jobAdded(JobDetail jobDetail) {
System.out.println("添加工作任务");
}
//删除工作任务
public void jobDeleted(JobKey jobKey) {
System.out.println("删除工作任务");
}
public void jobPaused(JobKey jobKey) {
// TODO Auto-generated method stub
}
public void jobsPaused(String jobGroup) {
// TODO Auto-generated method stub
}
public void jobResumed(JobKey jobKey) {
// TODO Auto-generated method stub
}
public void jobsResumed(String jobGroup) {
// TODO Auto-generated method stub
}
//scheduler产生Error调用
public void schedulerError(String msg, SchedulerException cause) {
// TODO Auto-generated method stub
}
//scheduler被挂起时候调用
public void schedulerInStandbyMode() {
// TODO Auto-generated method stub
}
//scheduler开启的时候调用
public void schedulerStarted() {
System.out.println("scheduler 开启 的时候调用");
}
//scheduler 正在开启的时候调用 ing.....
public void schedulerStarting() {
System.out.println("scheduler 正在开启的时候调用 ing.....");
}
// scheduler关闭 的时候调用
public void schedulerShutdown() {
System.out.println("scheduler关闭 的时候调用");
}
//scheduler 正在关闭的时候调用 ing.....
public void schedulerShuttingdown() {
System.out.println("//scheduler 正在关闭的时候调用 ing.....");
}
//scheduler 数据被清除了的时候调用
public void schedulingDataCleared() {
System.out.println("//scheduler 数据被清除了的时候调用");
}
}
将下述蓝色代码放于上述【Quartz的简单使用】对应位置
// 3、创建调度器Scheduler并执行
Scheduler scheduler = new StdSchedulerFactory().getScheduler();
scheduler.getListenerManager().addSchedulerListener(new MySchedulerListener());
scheduler.scheduleJob(jobDetail, trigger);
运行结果(注意看时间):
添加工作任务
获取触发器名称:trigger1
--------scheduler start ! ------------
at:23-08-22 00-37-43:391, prints: Hello scheduler
scheduler 正在开启的时候调用 ing.....
scheduler 开启 的时候调用
PrintWordsJob start at:23-08-22 00-37-43:395, prints: Hello Job-74
PrintWordsJob start at:23-08-22 00-37-44:294, prints: Hello Job-62
PrintWordsJob start at:23-08-22 00-37-45:301, prints: Hello Job-84
PrintWordsJob start at:23-08-22 00-37-46:292, prints: Hello Job-19
PrintWordsJob start at:23-08-22 00-37-47:301, prints: Hello Job-82
PrintWordsJob start at:23-08-22 00-37-48:288, prints: Hello Job-42
PrintWordsJob start at:23-08-22 00-37-49:296, prints: Hello Job-19
PrintWordsJob start at:23-08-22 00-37-50:298, prints: Hello Job-4
PrintWordsJob start at:23-08-22 00-37-51:290, prints: Hello Job-10
PrintWordsJob start at:23-08-22 00-37-52:294, prints: Hello Job-78
PrintWordsJob start at:23-08-22 00-37-53:292, prints: Hello Job-42
PrintWordsJob start at:23-08-22 00-37-54:298, prints: Hello Job-49
PrintWordsJob start at:23-08-22 00-37-55:291, prints: Hello Job-13
//scheduler 正在关闭的时候调用 ing.....
scheduler关闭 的时候调用
at:23-08-22 00-37-55:397, prints: Hello scheduler
--------scheduler shutdown ! ------------
九、定时任务参数传递问题
将下述蓝色代码放于上述【Quartz的简单使用】1和2之间的位置
// 1、创建JobDetail实例,并与PrintWordsJob类绑定(Job执行内容)
JobDetail jobDetail = JobBuilder.newJob(PrintWordsJob.class)
.withIdentity("job1", "group1").build();
// 传参
JobDataMap jobDataMap=jobDetail.getJobDataMap();
jobDataMap.put("name","传参test");
jobDataMap.put("age",11);
jobDataMap.put("sex","男");
// 2、构建Trigger实例,每隔1s执行一次
Trigger trigger = TriggerBuilder.newTrigger().withIdentity("trigger1", "triggerGroup1")
.startNow()// 立即生效
.withSchedule(SimpleScheduleBuilder.simpleSchedule()
.withIntervalInSeconds(1)// 每隔1s执行一次
.repeatForever()).build();// 一直执行
同时更新PrintWordsJob.java文件中的代码为以下内容:
import org.quartz.Job;
import org.quartz.JobDataMap;
import org.quartz.JobExecutionContext;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Random;
public class PrintWordsJob implements Job {
@Override
public void execute(JobExecutionContext jobExecutionContext) {
String printTime = new SimpleDateFormat("yy-MM-dd HH-mm-ss:SSS").format(new Date());
System.out.println("PrintWordsJob start at:" + printTime + ", prints: Hello Job-" + new Random().nextInt(100));
JobDataMap jobDataMap = jobExecutionContext.getJobDetail().getJobDataMap();
System.out.println(jobDataMap.get("name").toString() + ":" + jobDataMap.get("age").toString() +":"+ jobDataMap.get("sex").toString());
}
}
运行结果(注意看时间):
--------scheduler start ! ------------
at:23-08-22 01-00-24:165, prints: Hello scheduler
PrintWordsJob start at:23-08-22 01-00-24:176, prints: Hello Job-6
传参test:11:男
PrintWordsJob start at:23-08-22 01-00-25:114, prints: Hello Job-70
传参test:11:男
PrintWordsJob start at:23-08-22 01-00-26:106, prints: Hello Job-54
传参test:11:男
…
…
…
PrintWordsJob start at:23-08-22 01-00-36:104, prints: Hello Job-73
传参test:11:男
at:23-08-22 01-00-36:181, prints: Hello scheduler
--------scheduler shutdown ! ------------
十、任务操作
删除
将下述蓝色代码取代上述【Quartz的简单使用】TimeUnit.MILLISECONDS.sleep(12000);
这里休眠6秒后再执行删除job,运行当前删除方法可以看到6秒后直接job方法不再执行
// 睡眠6秒
TimeUnit.MILLISECONDS.sleep(6000);
// 删除
scheduler.deleteJob(JobKey.jobKey("job1","group1"));
// 睡眠6秒
TimeUnit.MILLISECONDS.sleep(6000);
运行结果(注意看时间):
--------scheduler start ! ------------
at:23-08-23 00-23-17:971, prints: Hello scheduler
PrintWordsJob start at:23-08-23 00-23-17:976, prints: Hello Job-13
PrintWordsJob start at:23-08-23 00-23-18:898, prints: Hello Job-1
PrintWordsJob start at:23-08-23 00-23-19:907, prints: Hello Job-89
PrintWordsJob start at:23-08-23 00-23-20:901, prints: Hello Job-17
PrintWordsJob start at:23-08-23 00-23-21:904, prints: Hello Job-74
PrintWordsJob start at:23-08-23 00-23-22:909, prints: Hello Job-70
PrintWordsJob start at:23-08-23 00-23-23:899, prints: Hello Job-42
at:23-08-23 00-23-29:990, prints: Hello scheduler
--------scheduler shutdown ! ------------
暂停、恢复
将下述蓝色代码取代上述【Quartz的简单使用】TimeUnit.MILLISECONDS.sleep(12000);
这里先暂停job,休眠6秒后再恢复job,运行程序可以看到6秒后先执行job方法6次后,正常时间间隔执行。
// 暂停
scheduler.pauseJob(JobKey.jobKey("job1","group1"));
// 睡眠6秒
TimeUnit.MILLISECONDS.sleep(6000);
// 恢复
scheduler.resumeJob(JobKey.jobKey("job1","group1"));
// 睡眠6秒
TimeUnit.MILLISECONDS.sleep(6000);
运行结果(注意看时间):
--------scheduler start ! ------------
at:23-08-23 00-26-55:737, prints: Hello scheduler
PrintWordsJob start at:23-08-23 00-27-01:747, prints: Hello Job-82
PrintWordsJob start at:23-08-23 00-27-01:747, prints: Hello Job-97
PrintWordsJob start at:23-08-23 00-27-01:747, prints: Hello Job-27
PrintWordsJob start at:23-08-23 00-27-01:747, prints: Hello Job-39
PrintWordsJob start at:23-08-23 00-27-01:747, prints: Hello Job-88
PrintWordsJob start at:23-08-23 00-27-01:747, prints: Hello Job-87
PrintWordsJob start at:23-08-23 00-27-01:747, prints: Hello Job-58
PrintWordsJob start at:23-08-23 00-27-02:666, prints: Hello Job-76
PrintWordsJob start at:23-08-23 00-27-03:670, prints: Hello Job-78
PrintWordsJob start at:23-08-23 00-27-04:678, prints: Hello Job-79
PrintWordsJob start at:23-08-23 00-27-05:667, prints: Hello Job-63
PrintWordsJob start at:23-08-23 00-27-06:676, prints: Hello Job-93
PrintWordsJob start at:23-08-23 00-27-07:667, prints: Hello Job-90
at:23-08-23 00-27-07:745, prints: Hello scheduler
--------scheduler shutdown ! ------------
参考资料:
1. https://blog.csdn.net/faramita_of_mine/article/details/123142384?ops_request_misc=&request_id=&biz_id=102&utm_term=quartz&utm_medium=distribute.pc_search_result.none-task-blog-2~all~sobaiduweb~default-6-123142384.142^v93^chatgptT3_2&spm=1018.2226.3001.4187
2. https://blog.csdn.net/yoonbongchi/article/details/110579024?ops_request_misc=&request_id=&biz_id=102&utm_term=quartz&utm_medium=distribute.pc_search_result.none-task-blog-2~all~sobaiduweb~default-3-110579024.142^v93^chatgptT3_2&spm=1018.2226.3001.4187
3. https://blog.csdn.net/m0_47010003/article/details/124709983?ops_request_misc=%257B%2522request%255Fid%2522%253A%2522169244769016800227480253%2522%252C%2522scm%2522%253A%252220140713.130102334..%2522%257D&request_id=169244769016800227480253&biz_id=0&utm_medium=distribute.pc_search_result.none-task-blog-2~all~top_click~default-2-124709983-null-null.142^v93^chatgptT3_2&utm_term=quartz&spm=1018.2226.3001.4187
相关文章:

Quartz任务调度框架介绍和使用
一、Quartz介绍 Quartz [kwɔːts] 是OpenSymphony开源组织在Job scheduling领域又一个开源项目,完全由Java开发,可以用来执行定时任务,类似于java.util.Timer。但是相较于Timer, Quartz增加了很多功能: 1.持久性作业 …...

drools8尝试
drools7升级到drools8有很大很大的变更.几乎不能说是一个项目了. 或者说就是名字相同的不同项目, 初看下来变化是这样 两个最关键的东西都retired了 https://docs.drools.org/8.42.0.Final/drools-docs/drools/migration-guide/index.html business central变成了一个VS code…...

【机器学习】python基础实现线性回归
手写梯度下降的实现ykxb的线性回归 算法步骤: (1)构造数据,y3*x5; (2)随机初始化和,任意数值,例如9,10; (3)计算,,并计算 (4&…...

vue table合并行 动态列名
需求: 1.合并行,相同数据合并 2,根据后端返回数据动态显示列名, 我这个业务需求是,每年增加一列,也就是列名不是固定的,后端返回数据每年会多一条数据,根据返回数据显示列名 实现: html <el-table v-loading"loading" :data"dataList" :span-metho…...

Spring Cloud Alibaba-Nacos Discovery--服务治理
1 服务治理介绍 先来思考一个问题 通过上一章的操作,我们已经可以实现微服务之间的调用。但是我们把服务提供者的网络地址 (ip,端口)等硬编码到了代码中,这种做法存在许多问题: 一旦服务提供者地址变化&am…...

【C++】unordered_map和unordered_set的使用 及 OJ练习
文章目录 前言1. unordered系列关联式容器2. map、set系列容器和unordered_map、unordered_set系列容器的区别3. unordered_map和unordered_set的使用4. set与unordered_set性能对比5. OJ练习5.1 在长度 2N 的数组中找出重复 N 次的元素思路分析AC代码 5.2 两个数组的交集思路分…...

初识 JVM 01
JVM JRE JDK的关系 JVM 的内存机构 程序计数器 java指令的执行流程: 1 右侧的java源代码编译为左侧的java字节码(右侧第一个方块对应左侧第一个方块) 2 字节码 经过解释器 变为机器码 3 机器码就可以被cpu来执行 程序计数器的作用就…...

FPGA应用学习笔记----I2S和总结
时序一致在慢时序方便得多 增加了时序分布和分析的复杂性 使用fifo会开销大量资源...

归并排序之从微观看递归
前言 这次,并不是具体讨论归并排序算法,而是利用归并排序算法,探讨一下递归。归并排序的特点在于连续使用了两次递归调用,这次我们将从微观上观察递归全过程,从本质上理解递归,如果能看完,你一…...
Pytorch-day07-模型保存与读取
PyTorch 模型保存&读取 模型存储模型单卡存储&多卡存储模型单卡读取&多卡读取 1、模型存储 PyTorch存储模型主要采用pkl,pt,pth三种格式,就使用层面来说没有区别PyTorch模型主要包含两个部分:模型结构和权重。其中模型是继承n…...
【C语言每日一题】01. Hello, World!
题目来源:http://noi.openjudge.cn/ch0101/01/ 01. Hello, World! 总时间限制: 1000ms 内存限制: 65536kB 问题描述 对于大部分编程语言来说,编写一个能够输出“Hello, World!”的程序往往是最基本、最简单的。因此,这个程序常常作为一个初…...

arm: day8
1.中断实验:按键控制led灯 流程: key.h /*************************************************************************> File Name: include/key.h> Created Time: 2023年08月21日 星期一 17时03分20秒***************************************…...

k8s容器加入host解析字段
一、通过edit或path来修改 kubectl edit deploy /xxxxx. x-n cattle-system xxxxx为你的资源对象名称 二、添加字段 三、code hostAliases:- hostnames:- www.rancher.localip: 10.10.2.180...
浅谈开发过程中完善的注释的重要性
第一部分:引言 1.1 简述编程注释的定义和功能 编程注释是一种在源代码中添加的辅助性文字,它不参与编译或执行,但对于理解源代码起着至关重要的作用。注释可以简单地描述代码的功能,也可以详细地解释算法的工作原理、设计决策的…...

Docker 微服务实战
1. 通过IDEA新建一个普通微服务模块 1.1 建Module docker_boot 1.2 改写pom <?xml version"1.0" encoding"UTF-8"?><project xmlns"http://maven.apache.org/POM/4.0.0" xmlns:xsi"http://www.w3.org/2001/XMLSchema-instance&…...

JupyterHub实战应用
一、JupyerHub jupyter notebook 是一个非常有用的工具,我们可以在浏览器中任意编辑调试我们的python代码,并且支持markdown 语法,可以说是科研利器。但是这种情况适合个人使用,也就是jupyter notebook以我们自己的主机作为服务器…...

【MySQL】视图
目录 一、什么是视图 二、视图的操作 2.1 创建视图 2.2 删除视图 三、视图规则和限制 一、什么是视图 视图是一个虚拟表,其内容由查询定义。同真实的表一样,视图包含一系列带有名称的列和行数据。视图的数据变化会影响到基表(创建视图所…...

基于 Android 剧院购票APP的开发与设计
摘要:近年来,随着社会的发展和科技方面的创新,越来越多的人选择使用手机应用程序来购买剧场票。本文将探讨基于 Android 平台的剧院购票应用程序的开发和设计。该应用程序将为用户提供浏览剧场列表、查看剧场详情、选择座位并购买剧场票的功能…...

反转链表II
江湖一笑浪滔滔,红尘尽忘了 题目 示例 思路 链表这部分的题,不少都离不开单链表的反转,参考:反转一个单链表 这道题加上哨兵位的话会简单很多,如果不加的话,还需要分情况一下,像是从头节点开始…...

HTML 和 CSS 来实现毛玻璃效果(Glassmorphism)
毛玻璃效果简介 它的主要特征就是半透明的背景,以及阴影和边框。 同时还要为背景加上模糊效果,使得背景之后的元素根据自身内容产生漂亮的“变形”效果,示例: 代码实现 首先,创建一个 HTML 文件,写入如下…...

手游刚开服就被攻击怎么办?如何防御DDoS?
开服初期是手游最脆弱的阶段,极易成为DDoS攻击的目标。一旦遭遇攻击,可能导致服务器瘫痪、玩家流失,甚至造成巨大经济损失。本文为开发者提供一套简洁有效的应急与防御方案,帮助快速应对并构建长期防护体系。 一、遭遇攻击的紧急应…...

深入剖析AI大模型:大模型时代的 Prompt 工程全解析
今天聊的内容,我认为是AI开发里面非常重要的内容。它在AI开发里无处不在,当你对 AI 助手说 "用李白的风格写一首关于人工智能的诗",或者让翻译模型 "将这段合同翻译成商务日语" 时,输入的这句话就是 Prompt。…...
Leetcode 3576. Transform Array to All Equal Elements
Leetcode 3576. Transform Array to All Equal Elements 1. 解题思路2. 代码实现 题目链接:3576. Transform Array to All Equal Elements 1. 解题思路 这一题思路上就是分别考察一下是否能将其转化为全1或者全-1数组即可。 至于每一种情况是否可以达到…...
多场景 OkHttpClient 管理器 - Android 网络通信解决方案
下面是一个完整的 Android 实现,展示如何创建和管理多个 OkHttpClient 实例,分别用于长连接、普通 HTTP 请求和文件下载场景。 <?xml version"1.0" encoding"utf-8"?> <LinearLayout xmlns:android"http://schemas…...

Cilium动手实验室: 精通之旅---20.Isovalent Enterprise for Cilium: Zero Trust Visibility
Cilium动手实验室: 精通之旅---20.Isovalent Enterprise for Cilium: Zero Trust Visibility 1. 实验室环境1.1 实验室环境1.2 小测试 2. The Endor System2.1 部署应用2.2 检查现有策略 3. Cilium 策略实体3.1 创建 allow-all 网络策略3.2 在 Hubble CLI 中验证网络策略源3.3 …...
多模态商品数据接口:融合图像、语音与文字的下一代商品详情体验
一、多模态商品数据接口的技术架构 (一)多模态数据融合引擎 跨模态语义对齐 通过Transformer架构实现图像、语音、文字的语义关联。例如,当用户上传一张“蓝色连衣裙”的图片时,接口可自动提取图像中的颜色(RGB值&…...

【SQL学习笔记1】增删改查+多表连接全解析(内附SQL免费在线练习工具)
可以使用Sqliteviz这个网站免费编写sql语句,它能够让用户直接在浏览器内练习SQL的语法,不需要安装任何软件。 链接如下: sqliteviz 注意: 在转写SQL语法时,关键字之间有一个特定的顺序,这个顺序会影响到…...
【论文笔记】若干矿井粉尘检测算法概述
总的来说,传统机器学习、传统机器学习与深度学习的结合、LSTM等算法所需要的数据集来源于矿井传感器测量的粉尘浓度,通过建立回归模型来预测未来矿井的粉尘浓度。传统机器学习算法性能易受数据中极端值的影响。YOLO等计算机视觉算法所需要的数据集来源于…...
OpenLayers 分屏对比(地图联动)
注:当前使用的是 ol 5.3.0 版本,天地图使用的key请到天地图官网申请,并替换为自己的key 地图分屏对比在WebGIS开发中是很常见的功能,和卷帘图层不一样的是,分屏对比是在各个地图中添加相同或者不同的图层进行对比查看。…...

RNN避坑指南:从数学推导到LSTM/GRU工业级部署实战流程
本文较长,建议点赞收藏,以免遗失。更多AI大模型应用开发学习视频及资料,尽在聚客AI学院。 本文全面剖析RNN核心原理,深入讲解梯度消失/爆炸问题,并通过LSTM/GRU结构实现解决方案,提供时间序列预测和文本生成…...