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

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

小区新冠疫情管理系统的设计与实现/基于springboot的小区疫情管理系统

摘要 采用更加便于维护和使用的Java语言&#xff0c;其可拓展性高且更富于表现力&#xff0c;基于mysql数据库、Springboot框架开发的小区新冠疫情管理系统&#xff0c;方便用户查看物资信息、疫苗信息。通过Eclipse来进行网页编程&#xff0c;其方便易用、移植适用性广、更加安…

Can‘t find end of central directory : is this a zip file ? at XMLHttpRequest

导出woed出现这个报错,原因其实很简单,路径写错了, 这个word首先必须是docx格式,然后必须放在public文件包下 如果放在public文件包下还没有用,则放在public包下 参考帖子: https://www.cnblogs.com/hejun26/p/13647927.html

渗透率超90%!智能座舱赛道迎来「存量」替代升级大周期

智能座舱赛道&#xff0c;正在迎来新一轮芯片替代潮。 相比于智能驾驶领域&#xff0c;座舱主机芯片市场并不「性感」&#xff0c;但巨大的存量替代升级机会&#xff0c;也不容小视。 高工智能汽车研究院监测数据显示&#xff0c;2023年1-6月中国市场&#xff08;不含进出口&am…

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

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

什么是有效的预测性维护 ?

在现代制造业的背景下&#xff0c;设备的可靠性和生产效率成为了企业追求的关键目标。而预测性维护&#xff08;Predictive Maintenance&#xff0c;简称PdM&#xff09;作为一种先进的维护策略&#xff0c;逐渐成为了实现这些目标的重要工具。然而&#xff0c;什么是有效的预测…

Windows wsl2支持systemd

背景 很多Linux发行版都是使用systemd来管理程序进程&#xff0c;但是在WSL中默认是用init来管理进程的。 为了符合长久的使用习惯&#xff0c;且省去不必要的学习成本&#xff0c;就在WSL的发行版&#xff08;我这里安装的是Ubuntu20.04&#xff09;中支持systemd&#xff0…

计算机视觉 -- 图像分割

文章目录 1. 图像分割2. FCN2.1 语义分割– FCN &#xff08;Fully Convolutional Networks&#xff09;2.2 FCN--deconv2.3 Unpool2.4 拓展–DeconvNet 3. 实例分割3.1 实例分割--Mask R-CNN3.2 Mask R-CNN3.3 Faster R-CNN与 Mask R-CNN3.4 Mask R-CNN&#xff1a;Resnet1013…

常用的Selenium基础使用模板和简单封装

前言 近来又用上了 Selneium &#xff0c;因为反复用到&#xff0c;所以在这里将一些常用的方法封装起来&#xff0c;方便后续的使用。 在这篇文章中&#xff0c;我们将探讨 Selenium 的基础模板和基础封装&#xff0c;以便更好地理解 Selenium 的使用方法。 在Selenium的使…

web---Vue2_语法学习

文章目录 1、Vue2 常用指令1.1 初始Vue1.2 Vue的两种模板语法1.3 数据绑定1.4 el和data的两种写法1.5 MVVM模型1.6 Vue中的数据代理1.7 事件处理--事件的基本使用1.7 事件处理--事件修饰符1.7 事件处理--键盘事件1.8 计算属性1.9 监视属性1.9 深度监视2.0 绑定css样式2.1 条件渲…

spring复习:(57)PropertyOverrideConfigurer用法及工作原理

一、属性配置文件 dataSource.urljdbc:mysql://xxx.xxx.xxx.xxx/test dataSource.usernameroot dataSource.passwordxxxxxx dataSource.driverClassNamecom.mysql.jdbc.Driver #dataSource.typecom.alibaba.druid.pool.DruidDataSource二、spring配置文件 <?xml version&…

NPM 管理组织成员

目录 1、向组织添加成员 1.1 邀请成员加入您的组织 1.2 撤销组织邀请 2、接收或拒接组织邀请 2.1 接收组织邀请 2.2 拒绝组织邀请 3、组织角色和权限 4、管理组织权限 5、从组织中删除成员 1、向组织添加成员 作为组织所有者&#xff0c;您可以将其他npm用户添加到…

[保研/考研机试] KY212 二叉树遍历 华中科技大学复试上机题 C++实现

题目链接&#xff1a; 二叉树遍历_牛客题霸_牛客网二叉树的前序、中序、后序遍历的定义&#xff1a; 前序遍历&#xff1a;对任一子树&#xff0c;先访问根&#xff0c;然后遍历其左子树&#xff0c;最。题目来自【牛客题霸】https://www.nowcoder.com/share/jump/43719512169…

【仿写tomcat】六、解析xml文件配置端口、线程池核心参数

线程池改造 上一篇文章中我们用了Excutors创建了线程&#xff0c;这里我们将它改造成包含所有线程池核心参数的形式。 package com.tomcatServer.http;import java.util.concurrent.*;/*** 线程池跑龙套** author ez4sterben* date 2023/08/05*/ public class ThreadPool {pr…

php 系列题目,包含查看后端源代码

一、弱类型比较问题 原则&#xff1a; 1.字符串和数字比较&#xff0c;字符串回被转换成数字。 "admin" 0&#xff08;true) admin被转换成数字&#xff0c;由于admin是字符串&#xff0c;转换失败&#xff0c;变成0 int(admin)0,所以比较结果是ture 2.混合字符串转…

RabbitMq的使用

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

百度云BOS云存储的图片如何在访问时,同时进行格式转换、缩放等处理

前言 之前做了一个图片格式转换和压缩的服务&#xff0c;结果太占内存。后来查到在访问图片链接时&#xff0c;支持进行图片压缩和格式转换&#xff0c;本来想着先格式转换、压缩图片再上传到BOS&#xff0c;现在变成了上传后&#xff0c;访问时进行压缩和格式转换。想了想&am…

无人机航管应答机 ping200XR

产品概述 ping200XR是一个完整的系统&#xff0c;旨在满足航管应答器和自动相关监视广播(ADS-B)的要求&#xff0c;在管制空域操作无人航空系统(UAS)。该系统完全可配置为模式A&#xff0c;模式C&#xff0c;模式S转发器和扩展ADS-B发射机的任何组合。ping200XR包括一个精度超…

AutoDev 1.1.3 登场,个性化 AI 辅助:私有化大模型、自主设计 prompt、定义独特规则...

在过去的半个月里&#xff0c;我们为开源辅助编程工具 AutoDev 添加了更强大的自定义能力&#xff0c;现在你可以&#xff1a; 使用自己部署的开源大模型自己配置 Intellij IDEA 中的行为自定义开发过程中的规范 当然了&#xff0c;如果您自身拥有开发能力的话&#xff0c;建议…

MinIO线上扩容实战

硬件投入肯定是随着业务的增长而增长&#xff0c;这就要求中间件平台必须提供水平伸缩机制&#xff0c;MinIO对象存储服务也不例外&#xff0c;本文就详细介绍MinIO的扩容。 Minio支持通过增加新的Server Pool来扩容老的集群。每个Server Pool都是一个相对独立的故障域&#x…

uniapp微信小程序点击右上角菜单分享功能权限配置

个人项目地址&#xff1a; SubTopH前端开发个人站 &#xff08;自己开发的前端功能和UI组件&#xff0c;一些有趣的小功能&#xff0c;感兴趣的伙伴可以访问&#xff0c;欢迎提出更好的想法&#xff0c;私信沟通&#xff0c;网站属于静态页面&#xff09; SubTopH前端开发个人站…