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

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

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

相关文章

HTML 和 CSS 来实现毛玻璃效果(Glassmorphism)

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

99页4万字XX大数据湖项目建设方案

导读&#xff1a;原文《99页4万字XX大数据湖项目建设方案》&#xff08;获取来源见文尾&#xff09;&#xff0c;本文精选其中精华及架构部分&#xff0c;逻辑清晰、内容完整&#xff0c;为快速形成售前方案提供参考。 目 录 1.项目综述 1.1.项目背景 1.2.项目目标 1.3.项…

详解!视频云存储/安防监控视频AI智能分析平台区域入侵/周界报警功能

区域入侵/周界报警入侵检测技术是TSINGSEE青犀智能分析平台推出的一种视频监控系统&#xff0c;可检测划定区域内是否有可疑人员并且在检测出这样的事件时生成警报。 视频监控/安防监控/视频存储TSINGSEE青犀视频智能分析平台可以在监控范围内划定特定区域&#xff0c;有人员入…

特征值分解、SVD分解在线性最小二乘解上的应用

1. 奇异值分解(SVD)原理 1.1 回顾特征值和特征向量 我们首先回顾下特征值和特征向量的定义如下&#xff1a; A x λ x Axλx Axλx其中A是一个nn的实对称矩阵&#xff0c;x是一个n维向量&#xff0c;则我们说λ是矩阵A的一个特征值&#xff0c;而x是矩阵A的特征值λ所对应的…

Linux:编写编译脚本Makefile文件

一、生成可执行文件 1、一个源文件编译 本例子主要区别.c及.cpp文件及编译该文件时使用的编译链。 1).c文件 // testadd.c #include <stdio.h> int main() {int a 1;int b 2;int sum a b;printf("sum %d\n", sum);return 0; }// Makefie GXX g CC gcc…

RabbitMq的使用

最近处理访客记录所以&#xff0c;来学习下rabbitMQ。之前同事已经写好了&#xff0c;这里只需要进行消费&#xff0c;后续会逐渐完善。 0.介绍 0.1交换机&#xff08;Exchanges&#xff09; rabbitmq中生产者发送的消息都是发送到交换机&#xff0c;再由交换机推入队列。所…

解决:Appium Inspector刷新页面一直加载转圈

目录 问题&#xff1a;Appium Inspector刷新页面一直加载转圈 解决办法&#xff1a; 1.进入设置页面-电池-后台耗电管理 2.找到下面3个应用&#xff0c;修改为允许后台高耗电 问题&#xff1a;Appium Inspector刷新页面一直加载转圈 1、手机进行操作后&#xff0c;Appium I…

Windows 11 下使用 VMWare Workstation 17 Pro 新建 CentOS Stream 9 64位 虚拟机 并配置网络

文章目录 为什么选择 CentOS Stream 9下载安装访问连接快照克隆网络配置 为什么选择 CentOS Stream 9 CentOS Linux 8: 已经过了 End-of-life (EOL)CentOS Linux 7: EOL Jun 30th, 2024CentOS Stream 8: EOL May 31st, 2024CentOS Stream 9: End of RHEL9 full support phase …

python中的matplotlib画折线图(数据分析与可视化)

先导包&#xff08;必须安装了numpy 、pandas 和matplotlib才能导包&#xff09;&#xff1a; import numpy as np import pandas as pd import matplotlib.pyplot as plt核心代码&#xff1a; import numpy as np import pandas as pd import matplotlib.pyplot as pltpd.se…

【Linux操作系统】Linux系统编程中信号捕捉的实现

在Linux系统编程中&#xff0c;信号是一种重要的机制&#xff0c;用于实现进程间通信和控制。当某个事件发生时&#xff0c;如用户按下CtrlC键&#xff0c;操作系统会向进程发送一个信号&#xff0c;进程可以捕获并相应地处理该信号。本篇博客将介绍信号的分类、捕获与处理方式…

前端需要理解的HTML知识

HTML&#xff08;超文本标记语言&#xff0c;HyperText Markup Language&#xff09;不是编程语言&#xff0c;而是定义了网页内容的含义和结构的标记语言。。“超文本”&#xff08;hypertext&#xff09;是指连接单个网站内或多个网站间的网页的链接。HTML 使用“标记”&…

2023年国赛数学建模思路 - 案例:随机森林

文章目录 1 什么是随机森林&#xff1f;2 随机深林构造流程3 随机森林的优缺点3.1 优点3.2 缺点 4 随机深林算法实现 建模资料 ## 0 赛题思路 &#xff08;赛题出来以后第一时间在CSDN分享&#xff09; https://blog.csdn.net/dc_sinor?typeblog 1 什么是随机森林&#xff…

C语言:选择+编程(每日一练Day6)

目录 ​编辑选择题&#xff1a; 题一&#xff1a; 题二&#xff1a; 题三&#xff1a; 题四&#xff1a; 题五&#xff1a; 编程题&#xff1a; 题一&#xff1a;至少是其他数字两倍的最大数 思路一&#xff1a; 思路二&#xff1a; 题二&#xff1a;两个数组的交集…

C#详解-Contains、StartsWith、EndsWith、Indexof、lastdexof

目录 简介: 过程: 举例1.1 举例1.2 ​ 总结: 简介: 在C#中Contains、StarsWith和EndWith、IndexOf都是字符串函数。 1.Contains函数用于判断一个字符串是否包含指定的子字符串&#xff0c;返回一个布尔值&#xff08;True或False&#xff09;。 2.StartsWith函数用于判断一…

将AI融入CG特效工作流;对谈Dify创始人张路宇;关于Llama 2的一切资源;普林斯顿LLM高阶课程;LLM当前的10大挑战 | ShowMeAI日报

&#x1f440;日报&周刊合集 | &#x1f3a1;生产力工具与行业应用大全 | &#x1f9e1; 点赞关注评论拜托啦&#xff01; &#x1f916; 将AI融入CG特效工作流&#xff0c;体验极致的效率提升 BV1pP411r7HY 这是 B站UP主 特效小哥studio 和 拓星研究所 联合投稿的一个AI特…

01、Cannot resolve MVC View ‘xxxxx前端页面‘

Cannot resolve MVC View ‘xxxxx前端页面’ 没有找到对应的mvc的前端页面。 代码&#xff1a;前端这里引入了 thymeleaf 模板 解决&#xff1a; 需要添加 thymeleaf 的依赖 <dependency><groupId>org.springframework.boot</groupId><artifactId>s…

Vue3 中引入液晶数字字体(通常用于大屏设计)

一、下载 .ttf 字体文件到本地&#xff0c;放在 src 中的 assets 文件下 下载液晶字体 DS-Digital.ttf 二、在 css 文件中引入字体 /* src/assets/fonts/dsfont.css */ font-face {font-family: electronicFont;src: url(./DS-Digital.ttf);font-weight: normal;font-styl…

Docker关于下载,镜像配置,容器启动,停止,查看等基础操作

系列文章目录 文章目录 系列文章目录前言一、安装Docker并配置镜像加速器二、下载系统镜像&#xff08;Ubuntu、 centos&#xff09;三、基于下载的镜像创建两个容器 &#xff08;容器名一个为自己名字全拼&#xff0c;一个为首名字字母&#xff09;四、容器的启动、 停止及重启…

【中危】Apache Ivy<2.5.2 存在XXE漏洞 (CVE-2022-46751)

漏洞描述 Apache Ivy 是一个管理基于 ANT 项目依赖关系的开源工具&#xff0c;文档类型定义(DTD)是一种文档类型定义语言,它用于定义XML文档中所包含的元素以及元素之间的关系。 Apache Ivy 2.5.2之前版本中&#xff0c;当解析自身配置、Ivy 文件或 Apache Maven 的 POM 文件…

数据结构与算法:通往编程高地的必修课(文末送书)

&#x1f935;‍♂️ 个人主页&#xff1a;艾派森的个人主页 ✍&#x1f3fb;作者简介&#xff1a;Python学习者 &#x1f40b; 希望大家多多支持&#xff0c;我们一起进步&#xff01;&#x1f604; 如果文章对你有帮助的话&#xff0c; 欢迎评论 &#x1f4ac;点赞&#x1f4…