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 文件,写入如下…...
【技术】国标GB28181视频平台EasyGBS通过对应密钥上传到其他平台展示的详细步骤
国标GB28181协议视频平台EasyGBS是基于国标GB28181协议的视频云服务平台,支持多路设备同时接入,并对多平台、多终端分发出RTSP、RTMP、FLV、HLS、WebRTC等格式的视频流。平台可提供视频监控直播、云端录像、云存储、检索回放、智能告警、语音对讲、平台级…...
SpeedBI数据可视化工具:浏览器上做分析
SpeedBI数据分析云是一种在浏览器上进行数据可视化分析的工具,它能够将数据以可视化的形式呈现出来,并支持多种数据源和图表类型。 所有操作,均在浏览器上进行 在浏览器中打开SpeedBI数据分析云官网,点击【免费使用】进入&#…...
8.21笔记
Deeplab-MSc-LargrFOC 此图除了主输出之外,还有五个支线输出,他们池化层与VGG网络不同,其中卷积核大小是3,而VGG中卷积核大小为2(这个网络一开始是基于VGG网络提出的,因为那时候提出比较早,没有…...
MyBatis-Plus中公共字段的统一处理
数据库中一些表的公共字段,例如修改时间、修改人、创建时间、创建人,我们一般都是这样来处理的: employee.setCreateTime(LocalDateTime.now()); employee.setUpdateTime(LocalDateTime.now()); employee.setCreateUser(UserHolder.get()); …...
SQL的导出与导入
1、导入 使用命令行导入 1.登录sql界面; 2.create database Demo新建一个库; 3.选中数据库use Demo;选中导入路径source D:Demo.sql; 4.查看表show tables; 2、导出 整个sql mysqldump -u username -ppassword dbname > dbname.sq…...
记录一次wordpress项目的发布过程
背景:发布一套已完成的代码到线上,有完整的代码包,sql文件,环境是linux 宝塔。无wordpress相关经验。 过程:正常的发布代码 问题1:访问自己的域名后跳转到别的域名。 解决: 修改数据表wp_optio…...
HTML详解连载(8)
HTML详解连载(8) 专栏链接 [link](http://t.csdn.cn/xF0H3)下面进行专栏介绍 开始喽浮动-产品区域布局场景 解决方法清除浮动方法一:额外标签发方法二:单伪元素法方法三:双伪元素法方法四:overflow浮动-总结…...
Linux系统之安装OneNav个人书签管理器
Linux系统之安装OneNav个人书签管理器 一、OneNav介绍1.OneNav简介2.OneNav特点 二、本地环境介绍2.1 本地环境规划2.2 本次实践介绍 三、检查本地环境3.1 检查本地操作系统版本3.2 检查系统内核版本3.3 检查本地yum仓库状态 四、安装httpd服务4.1 安装httpd4.2 启动httpd服务4…...
主程技术分享: 游戏项目帧同步,状态同步如何选
网络游戏开发项目中帧同步,状态同步如何选? 网络游戏的核心技术之一就是玩家的网络同步,主流的网络同步有”帧同步”与”状态同步”。今天我们来分析一下这两种同步模式。同时教大家如何在自己的项目中采用最合适的同步方式。接下来从以下3个方面来阐述: 对啦&…...
ChatGPT-4: 半年的深度使用思考
几个月的时间一直在使用 ChatGpt-4,以口述语音转文字的形式说一下自己的体会。 1、选择版本 大前提:我使用的都是 GPT4 的版本。也就是说至少每个月要付费20$。 因为 3.5 的版本,实际上使用体验是非常差的,主要体现在答非所问上。…...
如何自己做网站/app开发公司排行榜
Java 类路径Java 类路径告诉 java 解释器和 javac 编译器去哪里找它们要执行或导入的类。类(您可能注意到的那些 *.class 文件)可以存储在目录或 jar 文件中,或者存储在两者的组合中,但是只有在它们位于类路径中的某个地方时&…...
wordpress最新begin主题下载/会员制营销方案
文章目录商品列表页逻辑代码商品列表页的数据渲染商品详细页Ajax实现商品收藏商品列表页逻辑代码 commodity的views.py定义视图函数commodityView from django.core.paginator import Paginator, PageNotAnInteger,EmptyPage from django.http import HttpResponse from djan…...
衡阳网站排名优化/优化疫情政策
大数据作为重要的战略资源已经在全球范围内达成共识。根据GTM Research2015年的研究分析,到2020年,全世界电力大数据管理系统市场将达到38亿美元的规模。从2012年开始,英国、法国、美国等国家相继启动了大数据发展规划。再观国内,…...
免费模型网站/seo整合营销
转自:http://www.iteye.com/topic/470396 在开源java工具包里,最有名的当属apache commons。其中,以commons lang包最为开发者熟知。 但是它作为第三方包存在,或多或少给开发者带来一些不便利。 面包牛奶总是会有的,从…...
做外贸要开通哪个网站/南宁网络推广软件
http://blog.csdn.net/ma_jiang/article/details/5962179 1.su oracle 然后启动监听器 1.lsnrctl start 会看到启动成功的界面; 1.lsnrctl stop 停止监听器命令. 1.lsnrctl status 查看监听器命令. oracle数据库的安全包含两部分: 1.一部分是os的安全 2.网…...
网站搭建完手机访问/竞价代运营外包公司
目前 Servlet2.4和JSP2.0常用的有7个监听器接口,分为3类:1. Servlet上下文进行监听(Application级):用于监听 ServletContext 对象的创建和删除以及属性的添加、删除和修改等操作,该监听器需要用到如下两个接口类:(1) ServletContextAttribut…...