Quartz 定时任务使用 —— 入门简单调用(一)

Quartz 详细介绍

Quartz是一个开源的作业调度框架,它完全由Java写成,并设计用于J2SE和J2EE应用中。它提供了巨大的灵 活性而不牺牲简单性。你能够用它来为执行一个作业而创建简单的或复杂的调度。它有很多特征,如:数据库支持,集群,插件,EJB作业预构 建,JavaMail及其它,支持cron-like表达式等等。

Quartz框架是一个全功能、开源的任务调度服务,可以集成几乎任何的java应用程序—从小的单片机系统到大型的电子商务系统。Quartz可以执行上千上万的任务调度。

官网地址:http://www.quartz-scheduler.org

Quartz 核心概念

Job(任务)、JobDetail(任务细节)、Trigger(触发器)、Scheduler(任务调度器)

Job:其实Job是接口,查看源码就知道只有一个execute方法:

package org.quartz;

public interface Job {
    void execute(JobExecutionContext var1) throws JobExecutionException;
}

我们开发者只要实现此接口,实现execute方法即可。把我们想做的事情,在execute中执行即可。

JobDetail:Quartz执行Job时,需要新建个Job实例,但是不能直接操作Job类,所以通过JobDetail来获取Job的名称、描述信息。重要属性如下:

  • name:任务的名称。

  • group:任务所在的组(默认值:DEFAULT)。

  • jobClass:任务的实现类。

  • jobDataMap:传参的作用。

Trigger:执行任务的规则;比如每天,每小时等。

一般情况使用 SimpleTriggerCronTrigger,这个触发器实现了 Trigger 接口。

  • CronTrigger:对于复杂的时间表达式使用,比如每个月15日上午几点几分

  • SimpleTrigger:对于简单的时间表达式使用,比如每天执行几次

Scheduler:是最核心的概念,需要把JobDetail和Trigger注册到scheduler中,才可以执行。

下载jar包,本文内容使用的是最新的2.3版本

<dependency>
    <groupid>org.quartz-scheduler</groupid>
    <artifactid>quartz</artifactid>
    <version>2.2.1</version>
</dependency>
<dependency>
    <groupid>org.quartz-scheduler</groupid>
    <artifactid>quartz-jobs</artifactid>
    <version>2.2.1</version>
</dependency>

简单的示例

public class QuartzTest {
    public static void main(String[] args) {
        try {
            // 获取 Scheduler 调度器实例
            Scheduler scheduler = StdSchedulerFactory.getDefaultScheduler();

            // 启动调度
            scheduler.start();

            // 关闭调度
            scheduler.shutdown();

        } catch (SchedulerException se) {
            se.printStackTrace();
        }
    }
}

一旦您使用StdSchedulerFactory.getDefaultScheduler()获取调度程序,您的应用程序将不会终止,直到您调用scheduler.shutdown(),因为将有活动线程。

注意

不同的版本的jar包,具体的操作不太相同,但是思路是相同的;比如1.8.6jar包中,JobDetail是个类,直接通过构造方法与Job类关联。SimpleTrigger和CornTrigger是类;在2.0.2jar包中,JobDetail是个接口,SimpleTrigger和CornTrigger是接口

JavaSE 简单的搭建

项目结构图

log4j.xml,日志输出

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE log4j:configuration SYSTEM "log4j.dtd">

<log4j:configuration xmlns:log4j="http://jakarta.apache.org/log4j/">

  <appender name="default" class="org.apache.log4j.ConsoleAppender">
    <param name="target" value="System.out"/>
    <layout class="org.apache.log4j.PatternLayout">
      <param name="ConversionPattern" value="[%p] %d{dd MMM hh:mm:ss.SSS aa} %t [%c]%n%m%n%n"/>
    </layout>
  </appender>

 <logger name="org.quartz">
   <level value="info" />
 </logger>

  <root>
    <level value="debug" />
    <appender-ref ref="default" />
  </root>

</log4j:configuration>

HelloJob.java,具体执行的任务

import org.quartz.Job;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * 具体执行的任务
 */
public class HelloJob implements Job {
    Logger logger = LoggerFactory.getLogger(this.getClass());

    @Override
    public void execute(JobExecutionContext jobExecutionContext) throws JobExecutionException {

        System.out.println("Hello Job");
        // 此任务仅打印日志便于调试、观察
        this.logger.debug(this.getClass().getName() + " trigger...");
    }
}

Main方法测试

import org.quartz.*;
import org.quartz.impl.StdSchedulerFactory;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.concurrent.TimeUnit;

/**
 * 测试入口
 */
public class Bootstrap {
    private static Logger logger = LoggerFactory.getLogger(Bootstrap.class);

    public static void main(String[] args) {

        try {
            // 获取 Scheduler 调度器实例
            Scheduler scheduler = StdSchedulerFactory.getDefaultScheduler();

            // 启动调度
            scheduler.start();

            // 具体任务 JobDetail
            JobDetail job = JobBuilder.newJob(HelloJob.class).withIdentity("job1", "group1").build();

            // 触发时间点(每5秒执行一次)
            SimpleScheduleBuilder simpleScheduleBuilder = SimpleScheduleBuilder.simpleSchedule().withIntervalInSeconds(5).repeatForever();

            // 触发器
            Trigger trigger = TriggerBuilder.newTrigger().withIdentity("trigger1", "group1").startNow().withSchedule(simpleScheduleBuilder).build();

            // 交由Scheduler安排触发
            scheduler.scheduleJob(job, trigger);

            /* 为观察程序运行,此设置主程序睡眠1分钟才继续往下运行(因下一个步骤是“关闭Scheduler”) */
            try {
                TimeUnit.MINUTES.sleep(1);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }

            // 关闭调度
            scheduler.shutdown();

        } catch (SchedulerException se) {
            logger.error(se.getMessage(), se);
        }
    }
}

除了上面的SimpleTrigger,最常见的还有Cron表达式的用法,至于什么含义,在第三章中会有讲到

CronTrigger trigger = newTrigger().withIdentity("trigger1", "group1").withSchedule(cronSchedule("0/20 * * * * ?")).build();

Date ft = sched.scheduleJob(job, trigger);
log.info(job.getKey() + " 计划运行时间:" + ft + "cron表达式:" + trigger.getCronExpression());

执行调度的结果输出日志

INFO] 09 九月 04:32:17.318 下午 main [org.quartz.impl.StdSchedulerFactory]
Using default implementation for ThreadExecutor
[INFO] 09 九月 04:32:17.322 下午 main [org.quartz.simpl.SimpleThreadPool]
Job execution threads will use class loader of thread: main
[INFO] 09 九月 04:32:17.336 下午 main [org.quartz.core.SchedulerSignalerImpl]
Initialized Scheduler Signaller of type: class org.quartz.core.SchedulerSignalerImpl
[INFO] 09 九月 04:32:17.336 下午 main [org.quartz.core.QuartzScheduler]
Quartz Scheduler v.2.2.3 created.
[INFO] 09 九月 04:32:17.337 下午 main [org.quartz.simpl.RAMJobStore]
RAMJobStore initialized.
[INFO] 09 九月 04:32:17.338 下午 main [org.quartz.core.QuartzScheduler]
Scheduler meta-data: Quartz Scheduler (v2.2.3) 'DefaultQuartzScheduler' with instanceId 'NON_CLUSTERED'
  Scheduler class: 'org.quartz.core.QuartzScheduler' - running locally.
  NOT STARTED.
  Currently in standby mode.
  Number of jobs executed: 0
  Using thread pool 'org.quartz.simpl.SimpleThreadPool' - with 10 threads.
  Using job-store 'org.quartz.simpl.RAMJobStore' - which does not support persistence. and is not clustered.
[INFO] 09 九月 04:32:17.338 下午 main [org.quartz.impl.StdSchedulerFactory]
Quartz scheduler 'DefaultQuartzScheduler' initialized from default resource file in Quartz package: 'quartz.properties'
[INFO] 09 九月 04:32:17.338 下午 main [org.quartz.impl.StdSchedulerFactory]
Quartz scheduler version: 2.2.3
[INFO] 09 九月 04:32:17.338 下午 main [org.quartz.core.QuartzScheduler]
Scheduler DefaultQuartzScheduler_$_NON_CLUSTERED started.
Hello Job
[DEBUG] 09 九月 04:32:17.350 下午 DefaultQuartzScheduler_Worker-1 [com.anson.example1.HelloJob]
com.anson.example1.HelloJob trigger...
Hello Job
[DEBUG] 09 九月 04:32:22.344 下午 DefaultQuartzScheduler_Worker-2 [com.anson.example1.HelloJob]
com.anson.example1.HelloJob trigger...
Hello Job
[DEBUG] 09 九月 04:32:27.344 下午 DefaultQuartzScheduler_Worker-3 [com.anson.example1.HelloJob]
com.anson.example1.HelloJob trigger...
...
... 省略一下重复调度的hello Job内容日志 ...
...
[INFO] 09 九月 05:00:38.327 下午 main [org.quartz.core.QuartzScheduler]
Scheduler DefaultQuartzScheduler_$_NON_CLUSTERED shutting down.
[INFO] 09 九月 05:00:38.327 下午 main [org.quartz.core.QuartzScheduler]
Scheduler DefaultQuartzScheduler_$_NON_CLUSTERED paused.
[INFO] 09 九月 05:00:38.328 下午 main [org.quartz.core.QuartzScheduler]
Scheduler DefaultQuartzScheduler_$_NON_CLUSTERED shutdown complete.
Hello Job
[DEBUG] 09 九月 05:00:38.328 下午 DefaultQuartzScheduler_Worker-3 [com.anson.example1.HelloJob]
com.anson.example1.HelloJob trigger...

通过观察上面的日志可以看出一些默认的配置信息,比如何时开启调度,何时关闭调度,调度器的默认实例名DefaultQuartzScheduler ,默认的SimpleThreadPool执行的线程数位10个等等。

打开 quartz-2.2.3.jar 文件中的默认 quartz.properties 文件,对应的默认配置信息如下。

# Default Properties file for use by StdSchedulerFactory
# to create a Quartz Scheduler Instance, if a different
# properties file is not explicitly specified.
#

org.quartz.scheduler.instanceName: DefaultQuartzScheduler
org.quartz.scheduler.rmi.export: false
org.quartz.scheduler.rmi.proxy: false
org.quartz.scheduler.wrapJobExecutionInUserTransaction: false

org.quartz.threadPool.class: org.quartz.simpl.SimpleThreadPool
org.quartz.threadPool.threadCount: 10
org.quartz.threadPool.threadPriority: 5
org.quartz.threadPool.threadsInheritContextClassLoaderOfInitializingThread: true

org.quartz.jobStore.misfireThreshold: 60000

org.quartz.jobStore.class: org.quartz.simpl.RAMJobStore

如果在测试或生产环境中进行动态配置上吗的默认信息,可以在src目录下新建一个 quartz.properties 文件,配置内容来覆盖默认配置。

org.quartz.scheduler.instanceName = MyScheduler
org.quartz.threadPool.threadCount = 3

然后,在运行demo示例,会发现日志中的输出就会有所变化。

调度器的实例名变成了MyScheduler,线程的数量变成了3个,这意味着,同时最多可以运行3个Jobs

[INFO] 09 九月 04:56:54.685 下午 main [org.quartz.impl.StdSchedulerFactory]
Using default implementation for ThreadExecutor
[INFO] 09 九月 04:56:54.704 下午 main [org.quartz.core.SchedulerSignalerImpl]
Initialized Scheduler Signaller of type: class org.quartz.core.SchedulerSignalerImpl
[INFO] 09 九月 04:56:54.704 下午 main [org.quartz.core.QuartzScheduler]
Quartz Scheduler v.2.2.3 created.
[INFO] 09 九月 04:56:54.705 下午 main [org.quartz.simpl.RAMJobStore]
RAMJobStore initialized.
[INFO] 09 九月 04:56:54.706 下午 main [org.quartz.core.QuartzScheduler]
Scheduler meta-data: Quartz Scheduler (v2.2.3) 'MyScheduler' with instanceId 'NON_CLUSTERED'
  Scheduler class: 'org.quartz.core.QuartzScheduler' - running locally.
  NOT STARTED.
  Currently in standby mode.
  Number of jobs executed: 0
  Using thread pool 'org.quartz.simpl.SimpleThreadPool' - with 3 threads.
  Using job-store 'org.quartz.simpl.RAMJobStore' - which does not support persistence. and is not clustered.
[INFO] 09 九月 04:56:54.706 下午 main [org.quartz.impl.StdSchedulerFactory]
Quartz scheduler 'MyScheduler' initialized from default resource file in Quartz package: 'quartz.properties'
[INFO] 09 九月 04:56:54.706 下午 main [org.quartz.impl.StdSchedulerFactory]
Quartz scheduler version: 2.2.3
[INFO] 09 九月 04:56:54.707 下午 main [org.quartz.core.QuartzScheduler]
Scheduler MyScheduler_$_NON_CLUSTERED started.
Hello Job
[DEBUG] 09 九月 04:56:54.721 下午 MyScheduler_Worker-1 [com.anson.example1.HelloJob]
com.anson.example1.HelloJob trigger...
...
... 省略一下重复调度的hello Job内容日志 ...
...
[DEBUG] 09 九月 04:57:49.717 下午 MyScheduler_Worker-3 [com.anson.example1.HelloJob]
com.anson.example1.HelloJob trigger...
Hello Job
[DEBUG] 09 九月 04:57:54.719 下午 MyScheduler_Worker-1 [com.anson.example1.HelloJob]
com.anson.example1.HelloJob trigger...
[INFO] 09 九月 04:57:54.721 下午 main [org.quartz.core.QuartzScheduler]
Scheduler MyScheduler_$_NON_CLUSTERED shutting down.
[INFO] 09 九月 04:57:54.721 下午 main [org.quartz.core.QuartzScheduler]
Scheduler MyScheduler_$_NON_CLUSTERED paused.
[INFO] 09 九月 04:57:54.721 下午 main [org.quartz.core.QuartzScheduler]
Scheduler MyScheduler_$_NON_CLUSTERED shutdown complete.

如果你的英文够好的话,非常建议有必要阅读官方文档提供的示例代码跟着一起操作

http://www.quartz-scheduler.org/documentation/quartz-2.2.x/examples/

标题 描述
Example 1 - First Quartz Program "Hello World" for Quartz 入门
Example 2 - Simple Triggers  显示了十几种使用简单触发器调度作业的不同方式 
Example 3 - Cron Triggers 显示如何使用Cron Triggers来安排您的工作
Example 4 - Job State and Parameters 演示参数如何传递到job以及job如何维持状态
Example 5 - Handling Job Misfires 错过的任务怎么办
Example 6 - Dealing with Job Exceptions 调度程序的job抛出的异常处理
Example 7 - Interrupting Jobs  显示调度程序如何中断您的工作以及如何编写您的工作来处理中断
Example 8 - Fun with Calendars  演示了一个假期日历如何可以用来排除在假期的作业的执行
Example 9 - Job Listeners  使用job监听器让一个job触发另一个job,构建一个简单的工作流程
Example 10 - Using Quartz Plug-Ins  演示使用XML作业初始化插件以及历史记录插件
Example 11 - Quartz Under High Load  Quartz可以运行大量的job,但是看看线程池如何可以限制同时执行多少个job
Example 12 - Remote Job Scheduling using RMI  使用远程方法调用,Quartz调度程序可以由客户端远程调度
Example 13 - Clustered Quartz  演示如何在集群环境中使用Quartz,以及Quartz如何使用数据库来保留计划信息
Example 14 - Trigger Priorities 演示如何使用Trigger优先级来管理具有相同火灾时间的触发器的触发顺序
Example 15 - TC Clustered Quartz  演示Quartz如何与Terracotta集群,而不是数据库


赞(52) 打赏
未经允许不得转载:优客志 » JAVA开发
分享到:

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

微信扫一扫打赏