加载中...

QuarZ笔记

Quartz任务调度

一.Quartz概念

Quartz是OpenSymphony开源组织在Job scheduling领域又一个开源项目,它可以与J2EE与J2SE应用程序相结合也可以单独使用。

quartz是开源且具有丰富特性的"任务调度库",能够集成于任何的java应用,小到独立的应用,大至电子商业系统。quartz能够创建亦简单亦复杂的调度,以执行上十、上百,甚至上万的任务。任务job被定义为标准的java组件,能够执行任何你想要实现的功能。quartz调度框架包含许多企业级的特性,如JTA事务、集群的支持。

简而言之,quartz就是基于java实现的任务调度框架,用于执行你想要执行的任何任务。

 

官网:

http://www.quartz-scheduler.org/

二.Quartz运行环境

- Quartz 可以运行嵌入在另一个独立式应用程序

- Quartz 可以在应用程序服务器(或servlet容器)内被实例化,并且参与事务

- Quartz 可以作为一个独立的程序运行(其自己的Java虚拟机内),可以通过RMI使用

- Quartz 可以被实例化,作为独立的项目集群(负载平衡和故障转移功能),用于作业的执行

 

 三.Quartz设计模式

- Builder模式

- Factory模式

- 组件模式

- 链式编程

 

四.Quartz学习的核心概念

 - 任务Job

    Job就是你想要实现的任务类,每一个Job必须实现org.quartz.job接口,且只需实现接口定义的execute()方法。

 - 触发器Trigger

    Trigger为你执行任务的触发器,比如你想每天定时3点发送一份统计邮件,Trigger将会设置3点进行执行该任务。Trigger主要包含两种SimpleTrigger和CronTrigger两种。关于二者的区别的使用场景,后续的课程会进行讨论。

 - 调度器Scheduler

    Scheduler为任务的调度器,它会将任务job及触发器Trigger整合起来,负责基于Trigger设定的时间来执行Job。

五.Quartz的体系结构

 

 

 

.Quartz的几个常用API

以下是Quartz编程API几个重要接口,也是Quartz的重要组件

- Scheduler 用于与调度程序交互的主程序接口。

  Scheduler 调度程序-任务执行计划表,只有安排进执行计划的任务Job(通过scheduler.scheduleJob方法安排进执行计划),当它预先定义的执行时间到了的时候(任务触发trigger),该任务才会执行。

- Job 我们预先定义的希望在未来时间能被调度程序执行的任务类,我们可以自定义。

- JobDetail 使用JobDetail来定义定时任务的实例,JobDetail实例是通过JobBuilder类创建的。

- JobDataMap 可以包含不限量的(序列化的)数据对象,在job实例执行的时候,可以使用

  其中的数据;JobDataMap是Java Map接口的一个实现,额外增加了一些便于存取基本类型的数

  据的方法。

- Trigger 触发器,Trigger对象是用来触发执行Job的。当调度一个job时,我们实例一个触发器然后调整它的属性来满足job执行的条件。表明任务在什么时候会执行。定义了一个已经被安排的任务将会在什么时候执行的时间条件,比如每2秒就执行一次。

- JobBuilder -用于声明一个任务实例,也可以定义关于该任务的详情比如任务名、组名等,这个声明的实例将会作为一个实际执行的任务。

- TriggerBuilder 触发器创建器,用于创建触发器trigger实例。

- JobListener、TriggerListener、SchedulerListener监听器,用于对组件的监听。

 

七.Quartz的使用

1.准备工作

引入Quartz的jar包

1   <dependencies>
2      <dependency>
3      <groupId>org.quartz-scheduler</groupId>
4      <artifactId>quartz</artifactId>
5      <version>2.3.0</version>
6      </dependency>

2.入门案例

1)创建HelloJob任务类

   

 1  public class HelloJob implements Job {
 2     
 3         @Override
 4         public void execute(JobExecutionContext arg0) throws JobExecutionException {
 5             // 定义时间
 6             Date date = new Date();
 7             SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
 8             String dateString = dateFormat.format(date);
 9             // 定义工作任务内容
10             System.out.println("进行数据库备份操作。当前任务执行的时间:"+dateString);
11         }
12     }

2)创建任务调度类HelloSchedulerDemo---完成任务调度需要三要素:调度器(Scheduler)、任务实列(JobDetail)、触发器( Trigger)

 

 

 

 

 

 1 HelloSchedulerDemo.java
 2 
 3     public class HelloSchedulerDemo {
 4 
 5         public static void main(String[] args) throws Exception {
 6             // 1.调度器(Scheduler)--从工厂中获取任务调度的实例
 7             Scheduler scheduler = StdSchedulerFactory.getDefaultScheduler();
 8     
 9             // 2.任务实列(JobDetail)--定义一个任务调度实例,将该实例与HelloJob绑定,任务类需要实现Job接口
10             JobDetail job = JobBuilder.newJob(HelloJob.class)
11                     .withIdentity("job1", "group1") // 定义该实例唯一标识
12                     .build();
13     
14             // 3.定义触发器( Trigger) ,马上执行, 然后每5秒重复执行一次
15             Trigger trigger = TriggerBuilder.newTrigger()
16                     .withIdentity("trigger1", "group1") // 定义该实例唯一标识
17                     .startNow()  // 马上执行
18                     .withSchedule(SimpleScheduleBuilder.simpleSchedule()
19             .repeatSecondlyForever(5)) // 每5秒执行一次   
20                     .build();
21     
22             // 4:使用触发器调度任务的执行
23             scheduler.scheduleJob(job, trigger);
24     
25             // 5:开启
26             scheduler.start();
27             // 关闭
28             // scheduler.shutdown();
29         }
30     }

3)实现效果

 

 

4.Job和JobDetail介绍

 

- Job:工作任务调度的接口,任务类需要实现该接口。该接口中定义execute方法,类似JDK提供的TimeTask类的run方法。在里面编写任务执行的业务逻辑。

- Job实例在Quartz中的生命周期:每次调度器执行Job时,它在调用execute方法前会创建一个新的Job实例,当调用完成后,关联的Job对象实例会被释放,释放的实例会被垃圾回收机制回收。

- JobDetail:JobDetail为Job实例提供了许多设置属性,以及JobDetaMap成员变量属性,它用来存储特定Job实例的状态信息,调度器需要借助JobDetail对象来添加Job实例。

- JobDetail重要属性:name、group、jobClass、jobDataMap

 

1         JobDetail job = JobBuilder.newJob(HelloJob.class)
2                 .withIdentity("job1", "group1") // 定义该实例唯一标识,并指定一个组。
3                 .build();
4 
5         System.out.println("name:"+job.getKey().getName());
7         System.out.println("group:"+job.getKey().getGroup());
9         System.out.println("jobClass:"+job.getJobClass().getName());

5.JobExecutionContext介绍

- 当Scheduler调用一个Job,就会将JobExecutionContext传递给Job的execute()方法;

- Job能通过JobExecutionContext对象访问到Quartz运行时候的环境以及Job本身的明细数据。

 

 

 

 

6.JobDataMap介绍

 

1)使用Map获取。

 

- 在进行任务调度时,JobDataMap存储在JobExecutionContext中 ,非常方便获取。

 

- JobDataMap可以用来装载任何可序列化的数据对象,当job实例对象被执行时这些参数对象会传递给它。

 

 

 

- JobDataMap实现了JDK的Map接口,并且添加了非常方便的方法用来存取基本数据类型。

 

7.有状态的Job和无状态的Job

@PersistJobDataAfterExecution注解的使用

有状态的Job可以理解为多次Job调用期间可以持有一些状态信息,这些状态信息存储在JobDataMap中,而默认的无状态job每次调用时都会创建一个新的JobDataMap。

1)修改HelloSchedulerDemo.java。添加.usingJobData("count", 0),表示计数器。

 

1 JobDetail job = JobBuilder.newJob(HelloJob.class)
2             .withIdentity("job1", "group1") // 定义该实例唯一标识
3             .usingJobData("message", "打印日志")
4             .usingJobData("count", 0)
5             .build();

 

2)修改HelloJob.java

添加count的setting和getting方法。

1 private Integer count;
2     public void setCount(Integer count) {
3         this.count = count;
4     }

public void execute(JobExecutionContext context) throws JobExecutionException的方法中添加。

1      ++count;
2         System.out.println("count数量:"+count);
3         context.getJobDetail().getJobDataMap().put("count", count);

HelloJob类没有添加@PersistJobDataAfterExecution注解,每次调用时都会创建一个新的JobDataMap。不会累加;

HelloJob类添加@PersistJobDataAfterExecution注解,多次Job调用期间可以持有一些状态信息,即可以实现count的累加。

8.Trigger介绍

 

 

 

Quartz有一些不同的触发器类型,不过,用得最多的是SimpleTrigger和CronTrigger。

1)jobKey------表示job实例的标识,触发器被触发时,该指定的job实例会被执行。

2)startTime------表示触发器的时间表,第一次开始被触发的时间,它的数据类型是java.util.Date。

3)endTime------指定触发器终止被触发的时间,它的数据类型是java.util.Date。

案例:

 1 HelloJobTrigger.java
 2 
 3     // 定义任务类
 4     public class HelloJobTrigger implements Job {
 5     
 6         @Override
 7         public void execute(JobExecutionContext context) throws JobExecutionException {
 8             // 定义时间
 9             Date date = new Date();
10             SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
11             String dateString = dateFormat.format(date);
12             
13             // 定义工作任务内容
14             System.out.println("进行数据库备份操作。当前任务执行的时间:"+dateString);
15             
16             // 获取jobKey、startTime、endTime
17             Trigger trigger = context.getTrigger();
18             System.out.println("jobKey的标识:"+trigger.getJobKey().getName()+";jobKey的组名称:"+trigger.getJobKey().getGroup());
19             System.out.println("任务开始时间:"+dateFormat.format(trigger.getStartTime())+";任务结束时间:"+dateFormat.format(trigger.getEndTime()));
20         }
21     }
22 
23   HelloSchedulerDemoTrigger.java
24 
25     public class HelloSchedulerDemoTrigger {
26 
27         public static void main(String[] args) throws Exception {
28             // 1:从工厂中获取任务调度的实例
29             Scheduler scheduler = StdSchedulerFactory.getDefaultScheduler();
30             
31             // 定义日期
32             Date startDate = new Date();
33             // 启动任务,任务在当前时间3秒后执行
34             startDate.setTime(startDate.getTime()+3000);
35             // 定义日期
36             Date endDate = new Date();
37             // 结束任务,任务在当前时间10秒后停止
38             endDate.setTime(endDate.getTime()+10000);
39     
40             // 2:定义一个任务调度实例,将该实例与HelloJob绑定,任务类需要实现Job接口
41             JobDetail job = JobBuilder.newJob(HelloJobTrigger.class)
42                     .withIdentity("job1", "group1") // 定义该实例唯一标识
43                     .usingJobData("message", "打印日志")
44                     .build();
45     
46             // 3:定义触发器 ,马上执行, 然后每5秒重复执行一次
47             Trigger trigger = TriggerBuilder.newTrigger()
48                     .withIdentity("trigger1", "group1") // 定义该实例唯一标识
49                     .startAt(startDate)
50                     .endAt(endDate)
51                     .withSchedule(SimpleScheduleBuilder.simpleSchedule()
52                         .repeatSecondlyForever(5)) // 每5秒执行一次   
53                     .usingJobData("message", "simple触发器")
54                     .build();
55     
56             // 4:使用触发器调度任务的执行
57             scheduler.scheduleJob(job, trigger);
58             
59             // 5:开启
60             scheduler.start();
61             // 关闭
62             // scheduler.shutdown();
63         }
64     }

9.SimpleTrigger触发器

SimpleTrigger对于设置和使用是最为简单的一种 QuartzTrigger。

它是为那种需要在特定的日期/时间启动,且以一个可能的间隔时间重复执行 n 次的 Job 所设计的。

案例一:表示在一个指定的时间段内,执行一次作业任务;

 1 HelloJobSimpleTrigger.java
 2 
 3     // 定义任务类
 4     public class HelloJobSimpleTrigger implements Job {
 5 
 6 
 7         @Override
 8         public void execute(JobExecutionContext context) throws JobExecutionException {
 9             // 定义时间
10             Date date = new Date();
11             SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
12             String dateString = dateFormat.format(date);
13             
14             // 定义工作任务内容
15             System.out.println("进行数据库备份操作。当前任务执行的时间:"+dateString);
16         }
17     }
18 
19 HelloSchedulerDemoSimpleTrigger.java
20 
21     public class HelloSchedulerDemoSimpleTrigger {
22 
23         public static void main(String[] args) throws Exception {
24             // 1:从工厂中获取任务调度的实例
25             Scheduler scheduler = StdSchedulerFactory.getDefaultScheduler();
26             
27             // 定义日期
28             Date startDate = new Date();
29             // 启动任务,任务在当前时间3秒后执行
30             startDate.setTime(startDate.getTime()+3000);
31     
32             // 2:定义一个任务调度实例,将该实例与HelloJob绑定,任务类需要实现Job接口
33             JobDetail job = JobBuilder.newJob(HelloJobSimpleTrigger.class)
34                     .withIdentity("job1", "group1") // 定义该实例唯一标识
35                     .build();
36     
37             // 3:定义触发器 ,马上执行, 然后每5秒重复执行一次
38             Trigger trigger = TriggerBuilder.newTrigger()
39                     .withIdentity("trigger1", "group1") // 定义该实例唯一标识
40                     .startAt(startDate)
41                     .build();
42     
43             // 4:使用触发器调度任务的执行
44             scheduler.scheduleJob(job, trigger);
45             
46             // 5:开启
47             scheduler.start();
48             // 关闭
49             // scheduler.shutdown();
50         }
51     }

案例二:或在指定的时间间隔内多次执行作业任务。

修改HelloSchedulerDemoSimpleTrigger.java

1 // 3:定义触发器 ,马上执行, 然后每5秒重复执行一次
2         Trigger trigger = TriggerBuilder.newTrigger()
3                 .withIdentity("trigger1", "group1") // 定义该实例唯一标识
4                 .startAt(startDate)
5                 .withSchedule(SimpleScheduleBuilder.simpleSchedule().repeatSecondlyForever(5)
6                         .withRepeatCount(2)) // 每5秒执行一次,连续执行3次后停止,默认值是0
7                 .build();

案例三:指定任务的结束时间。

修改HelloSchedulerDemoSimpleTrigger.java

 1 // 定义日期
 2     Date endDate = new Date();
 3     // 启动结束,任务在当前时间10秒后停止
 4     endDate.setTime(endDate.getTime()+10000);
 5     
 6     // 2:定义一个任务调度实例,将该实例与HelloJob绑定,任务类需要实现Job接口
 7     JobDetail job = JobBuilder.newJob(HelloJobSimpleTrigger.class)
 8             .withIdentity("job1", "group1") // 定义该实例唯一标识
 9             .build();
10     
11     // 3:定义触发器 ,马上执行, 然后每5秒重复执行一次
12     Trigger trigger = TriggerBuilder.newTrigger()
13             .withIdentity("trigger1", "group1") // 定义该实例唯一标识
14             .startAt(startDate)
15             .endAt(endDate)
16             .withSchedule(SimpleScheduleBuilder.simpleSchedule().repeatSecondlyForever(5)
17                     .withRepeatCount(3)) // 每5秒执行一次,连续执行3次后停止
18             .build();    

需要注意的点

- SimpleTrigger的属性有:开始时间、结束时间、重复次数和重复的时间间隔。

- 重复次数属性的值可以为0、正整数、或常量 SimpleTrigger.REPEAT_INDEFINITELY。

- 重复的时间间隔属性值必须为大于0或长整型的正整数,以毫秒作为时间单位,当重复的时间间隔为0时,意味着与Trigger同时触发执行。

- 如果有指定结束时间属性值,则结束时间属性优先于重复次数属性,这样的好处在于:当我们需要创建一个每间隔10秒钟触发一次直到指定的结束时间的 Trigger,而无需去计算从开始到结束的所重复的次数,我们只需简单的指定结束时间和使用REPEAT_INDEFINITELY作为重复次数的属性 值即可。

 

CronTrigger触发器

如果你需要像日历那样按日程来触发任务,而不是像SimpleTrigger 那样每隔特定的间隔时间触发,CronTriggers通常比SimpleTrigger更有用,因为它是基于日历的作业调度器。

使用CronTrigger,你可以指定诸如“每个周五中午”,或者“每个工作日的9:30”或者“从每个周一、周三、周五的上午9:00到上午10:00之间每隔五分钟”这样日程安排来触发。甚至,象SimpleTrigger一样,CronTrigger也有一个startTime以指定日程从什么时候开始,也有一个(可选的)endTime以指定何时日程不再继续。

1)Cron Expressions——Cron 表达式

Cron表达式被用来配置CronTrigger实例。Cron表达式是一个由7个子表达式组成的字符串。每个子表达式都描述了一个单独的日程细节。这些子表达式用空格分隔,分别表示:

1. Seconds 秒

2. Minutes 分钟

3. Hours 小时

4. Day-of-Month 月中的天

5. Month 月

6. Day-of-Week 周中的天

7. Year (optional field) 年(可选的域)

取值:

 

 

 

单个子表达式可以包含范围或者列表。例如:前面例子中的周中的天这个域(这里是"WED")可以被替换为"MON-FRI", "MON, WED, FRI"或者甚至"MON-WED,SAT"。

所有的域中的值都有特定的合法范围,这些值的合法范围相当明显,例如:秒和分域的合法值为0到59,小时的合法范围是0到23,Day-of-Month中值得合法凡范围是1到31,但是需要注意不同的月份中的天数不同。月份的合法值是1到12。或者用字符串JAN,FEB MAR, APR, MAY, JUN, JUL, AUG, SEP, OCT, NOV 及DEC来表示。Days-of-Week可以用1到7来表示(1=星期日)或者用字符串SUN, MON, TUE, WED, THU, FRI 和SAT来表示.

 

练习一下:

   "0 0 10,14,16 * * ?" 每天上午10点,下午2点,4点
    "0 0/30 9-17 * * ?"   朝九晚五工作时间内每半小时,从0分开始每隔30分钟发送一次
    "0 0 12 ? * WED" 表示每个星期三中午12点 
    "0 0 12 * * ?" 每天中午12点触发 
    "0 15 10 ? * *" 每天上午10:15触发 
    "0 15 10 * * ?" 每天上午10:15触发 
    "0 15 10 * * ? *" 每天上午10:15触发 
    "0 15 10 * * ? 2005" 2005年的每天上午10:15触发 
    "0 * 14 * * ?" 在每天下午2点到下午2:59期间的每1分钟触发 
    "0 0/55 14 * * ?" 在每天下午2点到下午2:55期间,从0开始到55分钟触发 
    "0 0/55 14,18 * * ?" 在每天下午2点到2:55期间和下午6点到6:55期间的,从0开始到55分钟触发 
    "0 0-5 14 * * ?" 在每天下午2点到下午2:05期间的每1分钟触发 
    "0 10,44 14 ? 3 WED" 每年三月的星期三的下午2:10和2:44触发 
    "0 15 10 ? * MON-FRI" 周一至周五的上午10:15触发 
    "0 15 10 15 * ?" 每月15日上午10:15触发 
    "0 15 10 L * ?" 每月最后一日的上午10:15触发 
    "0 15 10 ? * 6L" 每月的最后一个星期五上午10:15触发 
    "0 15 10 ? * 6L 2002-2005" 2002年至2005年的每月的最后一个星期五上午10:15触发 
    "0 15 10 ? * 6#3" 每月的第三个星期五上午10:15触发 

案例:

HelloJobCronTrigger.java

 1 // 定义任务类
 2     public class HelloJobCronTrigger implements Job {
 3 
 4 
 5         @Override
 6         public void execute(JobExecutionContext context) throws JobExecutionException {
 7             // 定义时间
 8             Date date = new Date();
 9             SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
10             String dateString = dateFormat.format(date);
11             
12             // 定义工作任务内容
13             System.out.println("进行数据库备份操作。当前任务执行的时间:"+dateString);
14         }
15     }

HelloSchedulerDemoCronTrigger.java

 1 public class HelloSchedulerDemoCronTrigger {
 2 
 3         public static void main(String[] args) throws Exception {
 4             // 1:从工厂中获取任务调度的实例
 5             Scheduler scheduler = StdSchedulerFactory.getDefaultScheduler();
 6     
 7             // 2:定义一个任务调度实例,将该实例与HelloJob绑定,任务类需要实现Job接口
 8             JobDetail job = JobBuilder.newJob(HelloJobCronTrigger.class)
 9                     .withIdentity("job1", "group1") // 定义该实例唯一标识
10                     .build();
11     
12             // 3:定义触发器 ,马上执行, 然后每5秒重复执行一次
13             Trigger trigger = TriggerBuilder.newTrigger()
14                     .withIdentity("trigger1", "group1") // 定义该实例唯一标识
15                     .withSchedule(CronScheduleBuilder.cronSchedule("0/5 * * 6 4 ?"))// 定义表达式
16                     .build();
17     
18             // 4:使用触发器调度任务的执行
19             scheduler.scheduleJob(job, trigger);
20             
21             // 5:开启
22             scheduler.start();
23             // 关闭
24             // scheduler.shutdown();
25         }
26     }

小提示:

- ‘L’和‘W’可以一起使用。(企业可用在工资计算)

- ‘#’可表示月中第几个周几。(企业可用在计算母亲节和父亲节)

- 周字段英文字母不区分大小写,例如MON==mon。

- 利用工具,在线生成。

 

Quartz任务调度2

.配置、资源

SchedulerFactory

Quartz以模块方式构架,因此,要使它运行,几个组件必须很好的咬合在一起。幸运的是,已经有了一些现存的助手可以完成这些工作。

所有的Scheduler实例由SchedulerFactory创建

Quartz的三个核心概念:调度器、任务、触发器,三者之间的关系是:

 

 

 

大家都知道,一个作业,比较重要的三个要素就是Schduler,jobDetail,Trigger;而Trigger

对于job而言就好比一个驱动器;没有触发器来定时驱动作业,作业就无法运行;对于Job而言,

一个job可以对应多个Trigger,但对于Trigger而言,一个Trigger只能对应一个job;所以一

Trigger 只能被指派给一个 Job;如果你需要一个更复杂的触发计划,你可以创建多个 Trigger并指派它们给同一个 Job。

Scheduler的创建方式:

1)StdSchedulerFactory:

Quartz默认的SchedulerFactory

- 使用一组参数(java.util.Properties)来创建和初始化Quartz调度器

- 配置参数一般存储在quartz.properties文件中

- 调用getScheduler方法就能创建和初始化调度器对象

 

1 SchedulerFactory schedulerFactory = new StdSchedulerFactory();
2         Scheduler scheduler = schedulerFactory.getScheduler();

用法一:输出调度器开始的时间(重要:使得任务和触发器进行关联):

1    Date scheduleJob(JobDetail jobDetail, Trigger trigger)
2 
3     SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
4     System.out.println("调度器开始的时间是:"+dateFormat.format(scheduler.scheduleJob(job, trigger)));

用法二:启动任务调度:

1 void start();
2 scheduler.start();    

用法三:任务调度挂起,即暂停操作

1 void standby()
2 
3     // Scheduler执行2秒后自动挂起
4     Thread.sleep(2000L);
5     scheduler.standby();    
6     // Scheduler执行5秒后自动开启
7     Thread.sleep(5000L);
8     scheduler.start();    

用法四:关闭任务调度

void shutdown()

shutdown(true):表示等待所有正在执行的job执行完毕之后,再关闭Scheduler;

shutdown(false):表示直接关闭Scheduler

 

.Quartz.properties

默认路径:quartz-2.3.0中的org.quartz中的quartz.properties

 

 

 

我们也可以在项目的资源下添加quartz.properties文件,去覆盖底层的配置文件。

组成部分

- 调度器属性

org.quartz.scheduler.instanceName属性用来区分特定的调度器实例,可以按照功能用途来给调度器起名。

org.quartz.scheduler.instanceId属性和前者一样,也允许任何字符串,但这个值必须在所有调度器实例中是唯一的,尤其是在一个集群环境中,作为集群的唯一key。假如你想Quartz帮你生成这个值的话,可以设置为AUTO。

- 线程池属性

threadCount

处理Job的线程个数,至少为1,但最多的话最好不要超过100,在多数机器上设置该值超过100的话就会显得相当不实用了,特别是在你的 Job 执行时间较长的情况下

threadPriority

线程的优先级,优先级别高的线程比级别低的线程优先得到执行。最小为1,最大为10,默认为5

org.quartz.threadPool.class

一个实现了 org.quartz.spi.ThreadPool 接口的类,Quartz 自带的线程池实现类是 org.quartz.smpl.SimpleThreadPool

- 作业存储设置

描述了再调度器实例的生命周期中,Job和Trigger信息是如何被存储的。

- 插件配置

满足特定需求用到的Quartz插件的配置。

 1 例子:
 2 
 3     #===============================================================     
 4     #Configure Main Scheduler Properties     调度器属性
 5     #===============================================================  
 6     #调度器的实例名     
 7     org.quartz.scheduler.instanceName = QuartzScheduler     
 8     #调度器的实例ID,大多数情况设置为auto即可  
 9     org.quartz.scheduler.instanceId = AUTO     
10      
11     #===============================================================     
12     #Configure ThreadPool     线程池属性
13     #===============================================================   
14     #处理Job的线程个数,至少为1,但最多的话最好不要超过100,在多数机器上设置该值超过100的话就会显得相当不实用了,特别是在你的 Job 执行时间较长的情况下
15     org.quartz.threadPool.threadCount =  5     
16     #线程的优先级,优先级别高的线程比级别低的线程优先得到执行。最小为1,最大为10,默认为5
17     org.quartz.threadPool.threadPriority = 5 
18     #一个实现了 org.quartz.spi.ThreadPool 接口的类,Quartz 自带的线程池实现类是 org.quartz.smpl.SimpleThreadPool      
19     org.quartz.threadPool.class = org.quartz.simpl.SimpleThreadPool     
20      
21     #===============================================================     
22     #Configure JobStore 作业存储设置
23     #===============================================================      
24     #要使 Job 存储在内存中需通过设置  org.quartz.jobStrore.class 属性为 org.quartz.simpl.RAMJobStore 
25     org.quartz.jobStore.class = org.quartz.simpl.RAMJobStore     
26      
27     #===============================================================     
28     #Configure Plugins    插件配置 
29     #===============================================================       
30     org.quartz.plugin.jobInitializer.class =       
31     org.quartz.plugins.xml.JobInitializationPlugin       
32           
33     org.quartz.plugin.jobInitializer.overWriteExistingJobs = true      
34     org.quartz.plugin.jobInitializer.failOnFileNotFound = true      
35     org.quartz.plugin.jobInitializer.validating=false  

也可以编写程序代码操作quartz.properties文件的内容:

 1 public class QuartzProperties {
 2 
 3         public static void main(String[] args) {
 4             // 创建工厂实例
 5             StdSchedulerFactory factory = new StdSchedulerFactory();
 6             
 7             // 创建配置工厂的属性对象
 8             Properties props = new Properties();
 9             props.put(StdSchedulerFactory.PROP_THREAD_POOL_CLASS, "org.quartz.simpl.SimpleThreadPool"); // 线程池定义
10             props.put("org.quartz.threadPool.threadCount", "5"); // 默认Scheduler的线程数
11             
12             try {
13                 // 使用定义的属性初始化工厂
14                 factory.initialize(props);
15                 
16                 Scheduler scheduler = factory.getScheduler();
17                 
18                 scheduler.start();
19             } catch (SchedulerException e) {
20                 e.printStackTrace();
21             }
22         }
23     }

通过Properties设置工厂属性的缺点在用硬编码,假如需要修改例子中线程数量,将不得不修改代码,然后重新编译。我们这里不推荐使用。

 

 .Quartz监听器

1.概念

Quartz的监听器用于当任务调度中你所关注事件发生时,能够及时获取这一事件的通知。类似于任务执行过程中的邮件、短信类的提醒。Quartz监听器主要有JobListener、TriggerListener、SchedulerListener三种,顾名思义,分别表示任务、触发器、调度器对应的监听器。三者的使用方法类似,在开始介绍三种监听器之前,需要明确两个概念:全局监听器与非全局监听器,二者的区别在于:

全局监听器能够接收到所有的Job/Trigger的事件通知,

而非全局监听器只能接收到在其上注册的Job或Trigger的事件,不在其上注册的Job或Trigger则不会进行监听。

本课程关于全局与非全局的监听器的使用,将一一介绍。

2.JobListener

任务调度过程中,与任务Job相关的事件包括:job开始要执行的提示; job执行完成的提示。

 1 public interface JobListener {
 2 
 3           String getName();
 4           void jobToBeExecuted(JobExecutionContext context);
 5           void jobExecutionVetoed(JobExecutionContext context);
 6 
 7 
 8         void jobWasExecuted(JobExecutionContext context,
 9                 JobExecutionException jobException);
10     }

其中:

1) getName方法:用于获取该JobListener的名称。

2) jobToBeExecuted方法:Scheduler在JobDetail将要被执行时调用这个方法。

3) jobExecutionVetoed方法:Scheduler在JobDetail即将被执行,但又被TriggerListerner否决时会调用该方法

4) jobWasExecuted方法:Scheduler在JobDetail被执行之后调用这个方法

 

示例:

HelloJobListener.java

 

 1 // 定义任务类
 2     public class HelloJobListener implements Job {
 3         @Override
 4         public void execute(JobExecutionContext context) throws JobExecutionException {
 5             // 定义时间
 6             Date date = new Date();
 7             SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
 8             String dateString = dateFormat.format(date);
 9     
10             // 定义工作任务内容
11             System.out.println("进行数据库备份操作。当前任务执行的时间:"+dateString);
12         }
13     }

创建自定义的JobListener

MyJobListener.java

 1  public class MyJobListener implements JobListener{
 2         @Override
 3         public String getName() {
 4             String name = getClass().getSimpleName();
 5             System.out.println("监听器的名称是:"+name);
 6             return name;
 7         }
 8     
 9         @Override
10         public void jobToBeExecuted(JobExecutionContext context) {
11             String jobName = context.getJobDetail().getKey().getName();
12             System.out.println("Job的名称是:"+jobName+"     Scheduler在JobDetail将要被执行时调用这个方法");
13         }
14     
15         @Override
16         public void jobExecutionVetoed(JobExecutionContext context) {
17             String jobName = context.getJobDetail().getKey().getName();
18             System.out.println("Job的名称是:"+jobName+"     Scheduler在JobDetail即将被执行,但又被TriggerListerner否决时会调用该方法");
19         }
20     
21         @Override
22         public void jobWasExecuted(JobExecutionContext context, JobExecutionException jobException) {
23             String jobName = context.getJobDetail().getKey().getName();
24             System.out.println("Job的名称是:"+jobName+"     Scheduler在JobDetail被执行之后调用这个方法");
25         }
26     }

执行调度器

HelloSchedulerDemoJobListener.java

 1 public class HelloSchedulerDemoJobListener {
 2         public static void main(String[] args) throws Exception {
 3         // 1:从工厂中获取任务调度的实例
 4         Scheduler scheduler = StdSchedulerFactory.getDefaultScheduler();
 5     
 6         // 2:定义一个任务调度实例,将该实例与HelloJob绑定,任务类需要实现Job接口
 7         JobDetail job = JobBuilder.newJob(HelloJobListener.class)
 8                 .withIdentity("job1", "group1") // 定义该实例唯一标识
 9                 .build();
10 
11 
12           // 3:定义触发器 ,马上执行, 然后每5秒重复执行一次
13           Trigger trigger = TriggerBuilder.newTrigger()
14                   .withIdentity("trigger1", "group1") // 定义该实例唯一标识
15                   .startNow()  // 马上执行
16                   //.startAt(triggerStartTime) // 针对某个时刻执行
17                   .withSchedule(SimpleScheduleBuilder.simpleSchedule()
18                       .repeatSecondlyForever(5)) // 每5秒执行一次 
19                   .build();
20     
21           // 4:使用触发器调度任务的执行
22           scheduler.scheduleJob(job, trigger);
23     
24           // 创建并注册一个全局的Job Listener
25           scheduler.getListenerManager().addJobListener(new MyJobListener(), EverythingMatcher.allJobs());
26     
27           // 创建并注册一个指定任务的Job Listener
28           // scheduler.getListenerManager().addJobListener(new MyJobListener(),               KeyMatcher.keyEquals(JobKey.jobKey("job1", "group1")));
29           // 5:开启
30           scheduler.start();
31           // 关闭
32           // scheduler.shutdown();
33       }
34     }

3.TriggerListener

任务调度过程中,与触发器Trigger相关的事件包括:触发器触发、触发器未正常触发、触发器完成等。

 1 public interface TriggerListener {
 2 
 3     public String getName();
 4 
 5     public void triggerFired(Trigger trigger, JobExecutionContext context);
 6 
 7     public boolean vetoJobExecution(Trigger trigger, JobExecutionContext context);
 8 
 9     public void triggerMisfired(Trigger trigger);
10 
11     public void triggerComplete(Trigger trigger, JobExecutionContext context,
12             int triggerInstructionCode);
13 }

其中:

1) getName方法:用于获取触发器的名称

2) triggerFired方法:当与监听器相关联的Trigger被触发,Job上的execute()方法将被执行时,Scheduler就调用该方法。

3) vetoJobExecution方法:在 Trigger 触发后,Job 将要被执行时由 Scheduler 调用这个方法。TriggerListener 给了一个选择去否决 Job 的执行。假如这个方法返回 true,这个 Job 将不会为此次 Trigger 触发而得到执行。

4) triggerMisfired方法:Scheduler 调用这个方法是在 Trigger 错过触发时。你应该关注此方法中持续时间长的逻辑:在出现许多错过触发的 Trigger 时,长逻辑会导致骨牌效应。你应当保持这上方法尽量的小。

5) triggerComplete方法:Trigger 被触发并且完成了 Job 的执行时,Scheduler 调用这个方法。

 示例:

下面的例子简单展示了TriggerListener的使用,其中创建并注册TriggerListener与JobListener几乎类似。

HelloJobListener.java

 

 1 // 定义任务类
 2     public class HelloJobListener implements Job {
 3         @Override
 4         public void execute(JobExecutionContext context) throws JobExecutionException {
 5             // 定义时间
 6             Date date = new Date();
 7             SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
 8             String dateString = dateFormat.format(date);
 9     
10             // 定义工作任务内容
11             System.out.println("进行数据库备份操作。当前任务执行的时间:"+dateString);
12         }
13     }
14 MyTriggerListener.java
15 
16     public class MyTriggerListener implements TriggerListener{
17         private String name;
18     
19         public MyTriggerListener(String name) {
20                 this.name = name;
21         }
22     
23         @Override
24         public String getName() {
25             return name;
26         }
27     
28         @Override
29         public void triggerFired(Trigger trigger, JobExecutionContext context) {
30             String triggerName = trigger.getKey().getName();
31             System.out.println(triggerName + " 被触发");
32         }
33     
34         @Override
35         public boolean vetoJobExecution(Trigger trigger, JobExecutionContext context) {
36             String triggerName = trigger.getKey().getName();
37             System.out.println(triggerName + " 没有被触发");
38             return true; // true:表示不会执行Job的方法
39         }
40     
41         @Override
42         public void triggerMisfired(Trigger trigger) {
43             String triggerName = trigger.getKey().getName();
44             System.out.println(triggerName + " 错过触发");
45         }
46     
47         @Override
48         public void triggerComplete(Trigger trigger, JobExecutionContext context,
49                 CompletedExecutionInstruction triggerInstructionCode) {
50             String triggerName = trigger.getKey().getName();
51             System.out.println(triggerName + " 完成之后触发");
52         }
53     }

 

任务调度类HelloSchedulerDemoTriggerListener.java

 1 public class HelloSchedulerDemoTriggerListener {
 2         public static void main(String[] args) throws Exception {
 3             // 1:从工厂中获取任务调度的实例
 4             Scheduler scheduler = StdSchedulerFactory.getDefaultScheduler();
 5     
 6             // 2:定义一个任务调度实例,将该实例与HelloJob绑定,任务类需要实现Job接口
 7             JobDetail job = JobBuilder.newJob(HelloJobListener.class)
 8                     .withIdentity("job1", "group1") // 定义该实例唯一标识
 9                     .build();
10 
11 
12             // 3:定义触发器 ,马上执行, 然后每5秒重复执行一次
13             Trigger trigger = TriggerBuilder.newTrigger()
14                     .withIdentity("trigger1", "group1") // 定义该实例唯一标识
15                     .startNow()  // 马上执行
16                     .withSchedule(SimpleScheduleBuilder.simpleSchedule()
17                         .repeatSecondlyForever(5)) // 每5秒执行一次 
18                     .build();
19     
20             // 4:使用触发器调度任务的执行
21             scheduler.scheduleJob(job, trigger);
22     
23             // 创建并注册一个全局的Trigger Listener
24             scheduler.getListenerManager().addTriggerListener(new MyTriggerListener("simpleTrigger"), EverythingMatcher.allTriggers());
25     
26             // 创建并注册一个局部的Trigger Listener
27             // scheduler.getListenerManager().addTriggerListener(new MyTriggerListener("simpleTrigger"), KeyMatcher.keyEquals(TriggerKey.triggerKey("trigger1", "group1")));
28     
29             // 5:开启
30             scheduler.start();
31             // 关闭
32             // scheduler.shutdown();
33         }
34      }

4.SchedulerListener

SchedulerListener会在Scheduler的生命周期中关键事件发生时被调用。与Scheduler有关的事件包括:增加一个job/trigger,删除一个job/trigger,scheduler发生严重错误,关闭scheduler等。

 1 public interface SchedulerListener {
 2 
 3     public void jobScheduled(Trigger trigger);
 4 
 5     public void jobUnscheduled(String triggerName, String triggerGroup);
 6 
 7     public void triggerFinalized(Trigger trigger);
 8 
 9     public void triggersPaused(String triggerName, String triggerGroup);
10 
11     public void triggersResumed(String triggerName, String triggerGroup);
12 
13     public void jobsPaused(String jobName, String jobGroup);
14 
15     public void jobsResumed(String jobName, String jobGroup);
16 
17     public void schedulerError(String msg, SchedulerException cause);
18 
19     public void schedulerStarted();
20 
21     public void schedulerInStandbyMode();
22 
23     public void schedulerShutdown();
24 
25     public void schedulingDataCleared();
26 }

其中:

1) jobScheduled方法:用于部署JobDetail时调用

2) jobUnscheduled方法:用于卸载JobDetail时调用

3) triggerFinalized方法:当一个 Trigger 来到了再也不会触发的状态时调用这个方法。除非这个 Job 已设置成了持久性,否则它就会从 Scheduler 中移除。

4) triggersPaused方法:Scheduler 调用这个方法是发生在一个 Trigger 或 Trigger 组被暂停时。假如是 Trigger 组的话,triggerName 参数将为 null。

5) triggersResumed方法:Scheduler 调用这个方法是发生成一个 Trigger 或 Trigger 组从暂停中恢复时。假如是 Trigger 组的话,假如是 Trigger 组的话,triggerName 参数将为 null。参数将为 null。

6) jobsPaused方法:当一个或一组 JobDetail 暂停时调用这个方法。

7) jobsResumed方法:当一个或一组 Job 从暂停上恢复时调用这个方法。假如是一个 Job 组,jobName 参数将为 null。

8) schedulerError方法:在 Scheduler 的正常运行期间产生一个严重错误时调用这个方法。

9) schedulerStarted方法:当Scheduler 开启时,调用该方法

10) schedulerInStandbyMode方法: 当Scheduler处于StandBy模式时,调用该方法

11) schedulerShutdown方法:当Scheduler停止时,调用该方法

12) schedulingDataCleared方法:当Scheduler中的数据被清除时,调用该方法。

 

 示例:

下面的代码简单描述了如何使用SchedulerListener方法:

HelloJobListener.java

 1  public class HelloJobListener implements Job {
 2         @Override
 3         public void execute(JobExecutionContext context) throws JobExecutionException {
 4             // 定义时间
 5             Date date = new Date();
 6             SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
 7             String dateString = dateFormat.format(date);
 8     
 9             // 定义工作任务内容
10             System.out.println("进行数据库备份操作。当前任务执行的时间:"+dateString);
11         }
12     }

MySchedulerListener.java

  1  public class MySchedulerListener implements SchedulerListener{
  2         @Override
  3         public void jobScheduled(Trigger trigger) {
  4             String jobName = trigger.getJobKey().getName();
  5             System.out.println(jobName + " 完成部署");
  6         }
  7     
  8         @Override
  9         public void jobUnscheduled(TriggerKey triggerKey) {
 10             System.out.println(triggerKey + " 完成卸载");
 11         }
 12     
 13         @Override
 14         public void triggerFinalized(Trigger trigger) {
 15             System.out.println("触发器被移除 " + trigger.getJobKey().getName());
 16         }
 17     
 18         @Override
 19         public void triggerPaused(TriggerKey triggerKey) {
 20             System.out.println(triggerKey + " 正在被暂停");
 21         }
 22     
 23         @Override
 24         public void triggersPaused(String triggerGroup) {
 25             System.out.println("触发器组 "+triggerGroup + " 正在被暂停");
 26         }
 27     
 28         @Override
 29         public void triggerResumed(TriggerKey triggerKey) {
 30             System.out.println(triggerKey + " 正在从暂停中恢复");
 31         }
 32     
 33         @Override
 34         public void triggersResumed(String triggerGroup) {
 35             System.out.println("触发器组 "+triggerGroup + " 正在从暂停中恢复");
 36         }
 37     
 38         @Override
 39         public void jobAdded(JobDetail jobDetail) {
 40             System.out.println(jobDetail.getKey()+" 添加工作任务");
 41         }
 42     
 43         @Override
 44         public void jobDeleted(JobKey jobKey) {
 45             System.out.println(jobKey+" 删除工作任务");
 46         }
 47     
 48         @Override
 49         public void jobPaused(JobKey jobKey) {
 50             System.out.println(jobKey+" 工作任务正在被暂停");
 51         }
 52     
 53         @Override
 54         public void jobsPaused(String jobGroup) {
 55             System.out.println("工作任务组 "+jobGroup+" 正在被暂停");
 56         }
 57     
 58         @Override
 59         public void jobResumed(JobKey jobKey) {
 60             System.out.println(jobKey+" 正在从暂停中恢复");
 61         }
 62     
 63         @Override
 64         public void jobsResumed(String jobGroup) {
 65             System.out.println("工作任务组 "+jobGroup+" 正在从暂停中恢复");
 66         }
 67     
 68         @Override
 69         public void schedulerError(String msg, SchedulerException cause) {
 70             System.out.println("产生严重错误时调用:   "+msg+"  "+cause.getUnderlyingException());
 71         }
 72     
 73         @Override
 74         public void schedulerInStandbyMode() {
 75             System.out.println("调度器在挂起模式下调用");
 76         }
 77     
 78         @Override
 79         public void schedulerStarted() {
 80             System.out.println("调度器 开启时调用");
 81         }
 82     
 83         @Override
 84         public void schedulerStarting() {
 85             System.out.println("调度器 正在开启时调用");
 86         }
 87     
 88         @Override
 89         public void schedulerShutdown() {
 90             System.out.println("调度器 已经被关闭 时调用");
 91         }
 92     
 93         @Override
 94         public void schedulerShuttingdown() {
 95             System.out.println("调度器 正在被关闭 时调用");
 96         }
 97     
 98         @Override
 99         public void schedulingDataCleared() {
100             System.out.println("调度器的数据被清除时调用");
101         }
102     }

HelloSchedulerDemoSchedulerListener.java

 1 public class HelloSchedulerDemoSchedulerListener {
 2         public static void main(String[] args) throws Exception {
 3             // 1:从工厂中获取任务调度的实例
 4             Scheduler scheduler = StdSchedulerFactory.getDefaultScheduler();
 5     
 6             // 2:定义一个任务调度实例,将该实例与HelloJob绑定,任务类需要实现Job接口
 7             JobDetail job = JobBuilder.newJob(HelloJobListener.class)
 8                     .withIdentity("job1", "group1") // 定义该实例唯一标识
 9                     .build();
10 
11 
12             // 3:定义触发器 ,马上执行, 然后每5秒重复执行一次
13             Trigger trigger = TriggerBuilder.newTrigger()
14                     .withIdentity("trigger1", "group1") // 定义该实例唯一标识
15                     .startNow()  // 马上执行
16                     .withSchedule(SimpleScheduleBuilder.simpleSchedule()
17                         .repeatSecondlyForever(5)) // 每5秒执行一次 
18                     .build();
19     
20             // 4:使用触发器调度任务的执行
21             scheduler.scheduleJob(job, trigger);
22     
23             // 创建SchedulerListener
24             scheduler.getListenerManager().addSchedulerListener(new MySchedulerListener());
25     
26             // 移除对应的SchedulerListener
27             // scheduler.getListenerManager().removeSchedulerListener(new MySchedulerListener());
28     
29             // 5:开启
30             scheduler.start();
31             // 延迟7秒后关闭
32             Thread.sleep(7000);
33             // 关闭
34             scheduler.shutdown();
35         }
36      }

 

posted @ 2022-02-20 00:38  甜甜筒  阅读(170)  评论(0编辑  收藏  举报