目录
Scheduler调度程序、SchedulerFactory调度程序工厂
Quartz API
- Scheduler : 和调度程序交互的主要接口
- Job : 由调度程序执行的一个用来扩展的接口,execute方法里面执行的是job的具体行为
- JobDetail : 定义jobs的实例
- Trigger: 定义调度程序上的job的执行时间,用来设置任务什么时间触发
- JobBuilder: 用来创建或者定义JobDetail实例
- TriggerBuilder : 用来创建Trigger实例
Scheduler调度程序、SchedulerFactory调度程序工厂
scheduler调度程序
Scheduler维护一个jobDetails和triggers的注册表,当时间一到就会触发job执行
调度程序Scheduler可以通过SchedulerFactory工厂进行创建。一个已经创建的Scheduler可以通过SchedulerFactory获取。
Scheduler创建后只是出于待机状态,调用scheduler.start()方法启动,scheduler.shutdown方法关闭,isShutdown检查状态。
Scheduler调用scheduleJob方法将job加入调度,并在start方法执行后,触发job执行。
SchedulerFactory
SchedulerFactory有两个默认的实现类:DirectSchedulerFactory和StdSchedulerFactory
* DirectSchedulerFactory
DirectSchedulerFactory是一个org.quartz.SchedulerFactory的单例实现。
这里有一些使用DirectSchedulerFactory的示例代码段:
示例1:你可以使用createVolatileScheduler方法去创建一个不需要写入数据库的调度程序实例:
//创建一个拥有10个线程的调度程序
DirectSchedulerFactory.getInstance().createVolatileScheduler(10);
//记得启用该调度程序
DirectSchedulerFactory.getInstance().getScheduler().start();
另一种,创建方式:
public void createScheduler(String schedulerName, String schedulerInstanceId, ThreadPool threadPool, JobStore jobStore, String rmiRegistryHost, int rmiRegistryPort)
示例代码:
// 创建线程池
SimpleThreadPool threadPool = new SimpleThreadPool(maxThreads, Thread.NORM_PRIORITY);
threadPool.initialize();
// 创建job存储器
JobStore jobStore = new RAMJobStore();
//使用所有参数创建调度程序
DirectSchedulerFactory.getInstance().createScheduler("My Quartz Scheduler", "My Instance", threadPool, jobStore, "localhost", 1099);
// 不要忘了调用start()方法来启动调度程序
DirectSchedulerFactory.getInstance().getScheduler("My Quartz Scheduler", "My Instance").start();
也可以使用jdbcjobstore
DBConnectionManager.getInstance().addConnectionProvider("someDatasource", new JNDIConnectionProvider("someDatasourceJNDIName"));
JobStoreTX jdbcJobStore = new JobStoreTX(); jdbcJobStore.setDataSource("someDatasource"); jdbcJobStore.setPostgresStyleBlobs(true); jdbcJobStore.setTablePrefix("QRTZ_"); jdbcJobStore.setInstanceId("My Instance");
* StdSchedulerFactory
StdSchedulerFactory是org.quartz.SchedulerFactory的实现类,它是基于Quartz属性文件创建Quartz Scheduler 调度程序的。之前的入门案例使用的就是StdSchedulerFactory,因为我们指定了属性文件quartz.properties。
默认情况下是加载当前工作目录下的”quartz.properties”属性文件。如果加载失败,会去加载org/quartz包下的”quartz.properties”属性文件。我们使用JD-GUI反编译工具打开quartz.jar,可以在org/quartz包下找到其默认的属性文件的配置信息
默认的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
Job & JobDetail
Job是用来定义任务的执行行为,JobDetail则是用来创建并告知scheduler对应job的各种属性,JobDetail使用JobBuilder.newJob 创建,创建的时候必须知道job的实现类型。
任务Job有一个名称name 和组group 来关联。在一个Scheduler中这二者的组合必须是唯一的。
触发器任务计划执行表的执行”机制”。多个触发器可以指向同一个工作,但一个触发器只能指向一个工作。
JobDetail job = JobBuilder.newJob (HelloJob.class).withIdentity("job1", "group1")
.build();
public class HelloJob implements Job{
public HelloJob(){}
@Override
public void execute(JobExecutionContext paramJobExecutionContext)
throws JobExecutionException {
System.err.println("["+Thread.currentThread().getName()+"]"+"Hello World! MyJob is executing.");
}
}
每次scheduler执行job的execute方法都会创建一个新的实例,job执行完将它进行回收处理
当使用默认的SchedularFactory实现时,Job的实现类必须有一个无参数的构造器
Job的实现类中,定义一些状态变量是没有任何意义的,因为值不能在任务执行期间保留,因为每次执行一次,都会创建新的job实例,执行完毕就会销毁。
JobDataMap
JobDataMap是JobDetail实体的一部分,通过它能够了解Job在执行中的状态,并且用来存储一些和job执行过程中可用的数据
JobDataMap是map的一个实现类,提供了一些方法来存储和检索原始类型的数据
1. 在JobDatail创建的时候可以将数据放到JobDataMap中,在将job加到任务调度之前
Trigger trigger = TriggerBuilder.newTrigger()
.withIdentity("trigger1", "group1")
.usingJobData("name", "hello world! ")//usingJobData方法将数据放到jobDataMap中
.usingJobData("age", 110l)
.usingJobData("flag", false)
.startNow()
.withSchedule(SimpleScheduleBuilder.simpleSchedule()
.withIntervalInSeconds(1)
.repeatForever())
.build();
2. 在Job的执行期间将数据取出
public class HelloJob implements Job{
public HelloJob(){}
@Override
public void execute(JobExecutionContext context)
throws JobExecutionException {
JobDataMap jobDataMap = context.getJobDetail().getJobDataMap();
String name = jobDataMap.getString("name");
long age = jobDataMap.getLong("age");
boolean flag = jobDataMap.getBoolean("flag");
System.out.println("name:"+name+",age:"+age+",flag:"+flag);
System.err.println("["+Thread.currentThread().getName()+"]"+"Hello World! MyJob is executing.");
}
}
存储进去jobdataMap的数据将会被序列化,可能出现序列化的问题。显然标准的java类型存储是安全的,但是如果存放的是序列化自定义的对象,就可能出现序列化问题。
在存放key和字符串的模式中,可以将javadatamap作为数据存储,而不是存放基本类型数据,从而解决消除序列化出现的问题。
另外,Trigger也能和jobdatamap关联,当使用多个Trigger设置一个job时,对于每个trigger都可以通过jobdataMap提供独立的输入。
通过JobExecutionContext能够获取javadatamap,是jobdetail和trigger关联的javadatamap的合并,这这样获取的jobdataMap能够获取到jobDatail和Trigger触发器中的值。
Trigger trigger = TriggerBuilder.newTrigger()
.withIdentity("trigger1", "group1")
.usingJobData("name", "hello world! ")//usingJobData方法将数据放到jobDataMap中
.usingJobData("age", 110l)
.usingJobData("flag", false)
.startNow()
.withSchedule(SimpleScheduleBuilder.simpleSchedule()
.withIntervalInSeconds(1)
.repeatForever())
.build();
public void execute(JobExecutionContext context)
throws JobExecutionException {
JobDataMap jobDataMap = context.getMergedJobDataMap();
}
Job实例化的过程
可以创建一个job类,然后创建多个jobDetail,每个jobDetail都有自己的设置和jobdatamap,最后加到scheduler调度程序中。
举个例子:你可以创建一个SalesReportJob的job类,作为销售报表使用,在javadatamap中指定销售报表的名称和依据,然后创建这个job的多个jobDetail实例,例如 SalesReportForJoe和SalesReportForMik
重要:当触发器时间到了,就会加载关联的JobDetail,依赖于JobFactory的scheduler的配置进行实例化。JobFactory创建新的job实例,并调用job中set方法将值设置到jobdatamap的对应key上。jobFactory也可以扩展,通过ioc,di完成job实例的构建。
job的注解声明和并发
@DisallowConcurrentExecution: 添加在job类上,告知quartz在多线程环境下不执行job的多个实例。
注意,这个约束是基于jobDetail而不是job类,上面的SalesReportJob的job类,定义了多个SalesReportFroJoe和SalesReportForMik,这两个jobDetail实例,这就意味在多线程环境下, 只能执行一个SalesReportForJoe和一个SalesReportForMik定义的job.
@PersistJobDataAfterExecution: 当执行一次execute完毕后,更新jobdatamap中的数据,这样下次执行execute方法,检索的是更新的值,而不是原始的值。
如果使用@PersistJobDataAfterExecution注解,强烈建议配合使用@DisallowConcurrentExecution,避免在多线程环境下, 多个job导致的数据问题。
job的其他属性
- 持久化 - 如果一个任务不是持久化的,则当没有触发器关联它的时候,Quartz会从scheduler中删除它。
- 请求恢复 - 如果一个任务请求恢复,一般是该任务执行期间发生了系统崩溃或者其他关闭进程的操作,当服务再次启动的时候,会再次执行该任务。这种情况下,JobExecutionContext.isRecovering()会返回true
Trigger触发器
TriggerBuilder 用于创建触发器Trigger。如果你没有调用withSchedule(..) 方法,会使用默认的schedule 。如果没有使用withIdentity(..) 会自动生成一个触发器名称给你。可以在触发器使用TriggerBuilder进行实例化的同时设置一些属性
jobkey: 当触发器触发的时候,执行的job
starttime: 触发器开始触发的时间
endtime: 触发器结束触发的时间,比如每5天触发一次,endtime是7月1号,那么最后的执行时间是7月5号。
优先级Priority
设置属性优先级,只有starttime相同才有意义。
当一个任务请求恢复,恢复和原始的优先级一致。
参考:http://www.quartz-scheduler.org/documentation/quartz-2.1.x/tutorials/tutorial-lesson-04.html
https://blog.youkuaiyun.com/zixiao217/article/details/53053598