Quartz-任务调度

基础理论知识

        今天给大家介绍一个非常有名的java任务调度框架Quartz,所谓的任务(作业)调度的核心是支持“某个时间”执行“一个计划任务”;例如我们的财务系统,在每月月初、每个季度或者每年,都需要生成上一个月、季度或者去年的财务报表,而这些报表不可能你自己手动的去生成,这个时候就需要使用任务调度来完成。此框架可以使用Cron表达式更为灵活的来控制在某个时间来作业。
        其实任务调度框架很多,选择Quartz的原因它是一个开源的企业级任务调度服务,已经被作为任务调度的良好解决方案;而既然是任务调度自然离不开线程Quartz不仅仅提供线程和线程管理这样简单。为确保可伸缩性,Quartz采用了基于多线程的架构,这样就使Quartz可以并发多个任务。并且Quartz依赖一套松耦合的线程池来管理线程环境。
        Quartz的使用非常的简单,简单到让人难以相信。Quartz提出了三个核心概念--任务、触发器和调度器。我们只需要按照这三个核心来完成对应的接口实现与配置就可以实现任务调度了。

三个核心介绍

        1.任务-顾名思义就是到了某个时间段,我们要执行某个任务。比如生成报表的任务。
        2.触发器-既然任务有了,那么什么时间段触发呢?这个时候就需要有一个触发器。例如每个月月初执行生成报表任务,这就是触发器,触发时间是我们定义的触发规则。
        3.调度器-我们既然有了任务,也有了触发器,那么我们要怎么将这两个绑定在一起呢?这个时候就需要调度器。我们可以简单的理解成,调度器就是将一个触发器绑定到一个任务上,让触发器被触发后执行对应的任务。注意:调度器是由工厂创建的。

好了基础知识说完了下面我们开始一个简单的实例

1.创建Quartz任务

Quartz任务创建需要实现org.quartz.Job接口,并重写execute(JobExecutionContextcontext)方法。

import org.quartz.Job;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;
/** 此类只是用来执行任务代码 */
public class MyJob implements Job 
{ 
   @Override 
   public void execute(JobExecutionContext jobExecutionContext) throws JobExecutionException { 
      System.out.println("这里是我们的任务代码"); 
   }
}
2.创建Quartz任务

虽然我们上面已经写了类实现了Job接口,但是Quartz并不知道我们有这个任务所以我们需要告诉我们的MyJob这个类是一个任务

 //2.2.3版本 注册任务 方式 
JobDetail job = JobBuilder.newJob(MyJob.class)//设置要执行的任务是哪个类 
                          .withIdentity("helloJob", "group1")//设置任务信息 参数1任务名 参数2为组名
                          .build();//创建
3.创建触发器

我们使用的触发器主要有两种
        1.SimpleTrigger触发器
        2.CronTrigger触发器
两种触发器主要区别在于
        1.SimpleTrigger触发器
        通过设置触发器的属性:开始时间、结束时间、重复次数、重复间隔等。
        2.CronTrigger触发器
        可以使用cron表达式来更灵活的控制时间,例如每年执行一次、每天几点执行、每月几号执行等。

//Trigger 是触发器接口,要通过TriggerBuilder来实例化一个
TriggerTrigger trigger1 = TriggerBuilder.newTrigger()
                         //withIdentity(String name, String group) 指定触发器信息 参数1触发器名 参数2触发器组,如果不创建将自动生成
                         .withIdentity("trigger1", "test")
                          //withSchedule(ScheduleBuilder schedBuilder) 设置一个实现了ScheduleBuilder接口的触发器 
                         //这里我们演示的是SimpleTrigger触发器,所以要使用SimpleScheduleBuilder.simpleSchedule()方来创建 
                          .withSchedule(SimpleScheduleBuilder.simpleSchedule() 
                         //指定触发间隔,以秒为单位 
                         .withIntervalInSeconds(5)
                          //设置执行次数  
                         .withRepeatCount(5))
                         .build();//创建
Trigger trigger2 = TriggerBuilder.newTrigger() 
                         .withIdentity("cron trigger", "test")
                         .withSchedule(
                         //这里我们演示的是CronTrigger触发器,所以要使用CronScheduleBuilder.cronSchedule()方来创建
                         //每5秒执行一次
 CronScheduleBuilder.cronSchedule("0/5 * * ? * *")).build();
4.通过SchedulerFactory获取Scheduler调度器
//SchedulerFactory 提供了用于获得客户可用Scheduler的实例。
/** 
  * SchedulerFactory的一个实现,它执行所有的工作,基于一个Properties文件的内容创建一个QuartzScheduler实例。 
  * 默认情况下,从“当前工作目录”加载名为“quartz.properties”的属性文件。 
  * 如果失败,那么将加载位于org / quartz包中的(作为资源)的“quartz.properties”文件。 
  * 如果您希望使用除这些默认值之外的文件,则必须定义系统属性“org.quartz.properties”以指向所需的文件。 
  */
SchedulerFactory schedulerFactory = new StdSchedulerFactory();
try { 
 /** 
   * Scheduler是一个调度器 
   * 一个Scheduler注册JobDetail和Trigger。一旦注册后,Scheduler 负责执行Job,前提是Trigger的预定时间到达。
   */
   Scheduler scheduler = schedulerFactory.getScheduler();//调用getScheduler()方法来获取一个Scheduler实例
   //使用scheduleJob(JobDetail jobDetail, Trigger trigger) 方法使 任务 & 触发器 发生关联
    scheduler.scheduleJob(job, trigger2); 
   //使用start()方法开启调度 scheduler.start();
   //关闭调度//scheduler.shutdown();
} catch (SchedulerException e) 
{
   e.printStackTrace();
}

经过以上几步我们就配置好了一个任务,系统会每隔5秒输出这里是我们的任务代码下面我门将上述代码写入main()方法进行测试

import org.quartz.*;
import org.quartz.impl.StdSchedulerFactory;
/** 
  * 这是我的测试任务类 
  */
public class TestJob { 
   public static void main(String[] args) {
   //2.2.3版本 注册任务 方式
   JobDetail job = JobBuilder.newJob(MyJob.class) 
   //设置要执行的任务是哪个类 
   .withIdentity("helloJob", "group1") 
   //设置任务信息 参数1任务名 参数2为组名
   .build();//创建

   //Trigger 是触发器接口,要通过TriggerBuilder来实例化一个Trigger
   Trigger trigger1 = TriggerBuilder.newTrigger() 
   //withIdentity(String name, String group) 指定触发器信息 参数1触发器名 参数2触发器组,如果不创建将自动生成
   .withIdentity("trigger1", "test") //withSchedule(ScheduleBuilder schedBuilder) 设置一个实现了ScheduleBuilder接口的触发器 
   //这里我们演示的是SimpleTrigger触发器,所以要使用SimpleScheduleBuilder.simpleSchedule()方来创建 
   .withSchedule(SimpleScheduleBuilder.simpleSchedule() 
   //指定触发间隔,以秒为单位
   .withIntervalInSeconds(5)
   //设置执行次数
   .withRepeatCount(5))
   .build();//创建
   Trigger trigger2 = TriggerBuilder.newTrigger()
   .withIdentity("cron trigger", "test")
   .withSchedule(
   //这里我们演示的是CronTrigger触发器,所以要使用CronScheduleBuilder.cronSchedule()方来创建\
   //每5秒执行一次
   CronScheduleBuilder.cronSchedule("0/5 * * ? * *")).build();
   //SchedulerFactory 提供了用于获得客户可用Scheduler的实例。
   /**
     * SchedulerFactory的一个实现,它执行所有的工作,基于一个Properties文件的内容创建一个QuartzScheduler实例。
     * 默认情况下,从“当前工作目录”加载名为“quartz.properties”的属性文件。
     * 如果失败,那么将加载位于org / quartz包中的(作为资源)的“quartz.properties”文件。
     * 如果您希望使用除这些默认值之外的文件,则必须定义系统属性“org.quartz.properties”以指向所需的文件。 
     */
   SchedulerFactory schedulerFactory = new StdSchedulerFactory();
   try {
     /** 
       * Scheduler是一个调度器
       * 一个Scheduler注册JobDetail和Trigger。一旦注册后,Scheduler 负责执行Job,前提是Trigger的预定时间到达。 
       */
       Scheduler scheduler = schedulerFactory.getScheduler();//调用getScheduler()方法来获取一个Scheduler实例
       //使用scheduleJob(JobDetail jobDetail, Trigger trigger) 方法使 任务 & 触发器 发生关联
       scheduler.scheduleJob(job, trigger2);
       //使用start()方法开启调度
       scheduler.start();
       //关闭调度//scheduler.shutdown();
   } catch (SchedulerException e)
   { 
       e.printStackTrace();
   } 
  }
}
扯扯细节

        上面就是Quartz2.2.3版本的使用方法,每句代码的意思已为读者姥爷奉上。细心的读者姥爷可能会问execute(JobExecutionContext jobExecutionContext)方法中的JobExecutionContext接口是干什么的?有什么用?
        下面我们就来扯一扯JobExecutionContext接口包含任务执行的上下文信息,在执行时将其提供给JobDetail实例,并在执行完成后传递给Trigger实例。
        此接口只有一个JobExecutionContextImpl实例,而JobExecutionContextImpl实例的getJobDetail()方法可以得到JobDetail与之关联的Job。
        再通过getJobDataMap()方法来获取JobDataMap与该相关联Job。我门就可以获取传递过来的信息了。

两个栗子
栗子1
//System.out.println("这里是我们的任务代码");
JobDataMap jobDataMap = jobExecutionContext.getJobDetail().getJobDataMap();
//JobDataMap类似于Map集合,所以通过 键 来取 值
System.out.println("猴子的姓名:" + jobDataMap.get("name"));

好了取值说完了,现在说说怎么将值给传递过来。上面我们说过jobExecutionContext.getJobDetail()
方法是为了获取得到JobDetail与之关联的Job,也就是说我们需在在创建完Job之后获得JobDataMap对象,然后以键值对的方式来将值保存。

栗子2
JobDetail job = JobBuilder.newJob(MyJob.class)
      //设置要执行的任务是哪个类 
      .withIdentity("helloJob", "group1")//设置任务信息 参数1任务名 参数2为组名
      .build();//创建
job.getJobDataMap().put("name", "孙悟空");

上面一部分代码是我们在 main()方法中创建的任务,job.getJobDataMap().put("name", "孙悟空");
用来获取JobDataMap对象后设置键值。

到此Quartz就结束了,这里写的不是很深入希望各位读者姥爷多多谅解。由于我是JavaWeb开发,所以还会写一篇Spring整合Quartz的例子。如果你使用的是低版本,如1.8.6版本请看一下例子。

1.8.6栗子

//参数1任务名 参数2为组名 参数3类字节码
JobDetail job = new JobDetail("helloJob", "group1", MyJob.class);
job.getJobDataMap().put("name", "孙悟空");
//参数1触发器名,参数2组名,参数3执行次数 -1 为无限制,参数4 每个多少秒执行一次
Trigger trigger1 = new SimpleTrigger("trigger1", "group", 5, 5 * 1000);
Trigger trigger2 = null;
try {
   //参数1调度器名,参数2组名,参数3Cron表达式
   trigger2 = new CronTrigger("cron trigger", "group", "0/5 * * ? * *");
} catch (ParseException e) {
   e.printStackTrace();
}

SchedulerFactory schedulerFactory = new StdSchedulerFactory();
try { 
   Scheduler scheduler = schedulerFactory.getScheduler();
   scheduler.scheduleJob(job, trigger2); 
   scheduler.start();
} catch (SchedulerException e)
{ 
   e.printStackTrace();
}

可以看出SchedulerFactory的方式没有变只是JobDetail和Trigger的创建方式变了。

注意:
实例化SimpleTrigger与CronTrigger的时候组名可以不需要。根据情况而定,我个人认为还是写上。

配图
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,590评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 86,808评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,151评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,779评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,773评论 5 367
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,656评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,022评论 3 398
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,678评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 41,038评论 1 299
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,659评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,756评论 1 330
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,411评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,005评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,973评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,203评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,053评论 2 350
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,495评论 2 343

推荐阅读更多精彩内容