目录
一、认识 Quartz
Quartz是一个轻量级的,通过配置文件的来配置并且具有容错能力的任务调度框架。(容错能力指的是当你重启程序的时候定时任务不会丢失)。
拥有主接口Schedule主要负责安排任务,取消任务,开启任务,停止任务。
使用Quartz的时候要实现job接口?Job接口里面有一个execute()方法。
当组件实现TriggerListener或者JobListenter就可以在到达定时任务的时候通知想要通知的人。
存储空间的大小(RAM的数量和磁盘的大小)是限制Job和Trigger的存储和监听数量的大小。
SimpTrigger使用在任何毫秒级时间内,指定多久执行一次,可以每N秒执行一次,与当天的时刻无关。
ConTrigger 可以指定当天的某时刻执行,按照“公历”时间来计算,可以指定一天中的时间触发,然后计算下一次触发的时间。
二、使用Quartz(maven版)
1、在官网http://www.quartz-scheduler.org/downloads/下载需要的版本jar包(密码:6znr),在当前服务的pom.xml配置
<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>
2、在目录src/main/resources目录放置quartz.properties文件并配置
//设置调度程序scheduler的名称为MyScheduler
org.quartz.scheduler.instanceName=XXXScheduler
//线程里设置了3个线程,意味着最多同时运行4个job
org.quartz.threadPool.threadCount=4
/**指定为RAMJobStore,表示所有Quartz的数据,
包括jobDetail、trigger等均运行在内存中(而不是数据库中)。
如果你想Quartz使用你的数据库,
还是建议你在使用数据库配置之前使用RAMJobStore进行工作。
通过使用一个数据库,你可以打开一个全新的维度,
但在这之前,建议你使用RAMJobStore。
*/
org.quartz.jobStore.class=org.quartz.simpl.RAMJobStore
org.quartz.threadPool.class=org.quartz.simpl.SimpleThreadPool
ADD_NEW_COMPANY_USER_JUDGE_JOB_CRON=0 */5 * * * ?
3、编写一个job类,需要实现org.quartz.Job接口
public class XXXJob implements Job{
@Override
public void execute(JobExecutionContext context) throws JobExecutionException {
//要执行的操作
System.out.println("现在是北京时间:" + DateTimeHelper.formatTime(System.currentTimeMillis()) + " - helloJob任务执行");
}
}
4、使用job、trigger、schedule调用定时任务
public class TestSchedule extends HttpServlet {
public static void main(String[] args) throws Exception {
/*ScheduledExecutorService scheduledExecutorService = Executors.newScheduledThreadPool(1);
scheduledExecutorService.scheduleAtFixedRate(setCommand(),getTime(),5, TimeUnit.SECONDS);
*/
Scheduler scheduler = StdSchedulerFactory.getDefaultScheduler();
System.out.println("scheduleName = " + scheduler.getSchedulerName());
/** 重要:
* 定义一个job,并绑定到我们自定义的HelloJob的class对象
* 这里并不会马上创建一个HelloJob实例,实例创建是在scheduler安排任务触发执行时创建的
* 这种机制也为后面使用Spring集成提供了便利
*/
JobDetail job = newJob(XXXJob.class)
.withIdentity("job1", "group1")
.build();
Trigger trigger = newTrigger()
.withIdentity("trigger1", "group1")
.startNow()
.withSchedule(simpleSchedule()
.withIntervalInSeconds(2)
.repeatForever())
.build();
//告诉quartz使用定义的触发器trigger安排执行任务job
scheduler.scheduleJob(job,trigger);
//启动任务调度程序,内部机制是线程的启动
scheduler.start();
//关闭任务调度程序,如果不关闭,调度程序schedule会一直运行着
//scheduler.shutdown();
}
使用CronTrigger来指定某时间进行批跑
StdSchedulerFactory schedulerFactory = new StdSchedulerFactory();
Scheduler scheduler = schedulerFactory.getScheduler();
JobDetail job = newJob(XXXJob.class)
.withIdentity("job1", "group1")
.build();
CronTrigger trigger = (CronTrigger) newTrigger()
.withIdentity("trigger1", "group1")
.startNow()
.withSchedule(cronSchedule("0 13 0-22 * * ?"))
.build();
scheduler.scheduleJob(job,trigger);
scheduler.start();
三、理解Quartz这家伙
Scheduler调度任务-执行计划表,一定是通过scheduler.scheduleJob(job,trigger),当他指定的执行时间到了时间(任务触发Trigger)才执行任务。
- Scheduler是与任务程序交互的主程序接口
StdSchedulerFactory是org.quartz.SchedulerFactory的实现类, 它是基于Quartz属性文件创建Quartz Scheduler 调度程序的。 默认情况下是加载当前工作目录下的”quartz.properties”属性文件。 如果加载失败就会默认的加载org.quartz文件下面的”quartz.properties”属性文件。 你可以下载JD-GUI反编译工具打开quartz.jar。 当没有指定属性配置文件的话就会默认加载 DefaultQuartzScheduler 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 SchedulerFactory调用程序工场创建了Scheduler实例,而Scheduler调用程序。 创建的Scheduler是处于等待状态,在指定任务时间之前调用了Scheduler.start()来启用调度程序, 才能够执行定时任务。其中shudow()方法用来关闭,而isShutDown()可以用来判断是否关闭。 通过Scheduler的scheduleJob(…)方法的几个重载方法将任务纳入调度程序中。 使用scheduleJob(JobDetail jobDetail, Trigger trigger)来将定时任务安排进调度计划中。 Scheduler维护了JobDetail和Trigger注册表,在Scheduler注册过的job, 当定时任务触发时间到了,就会有调度程序去执行这个任务的job。 stdSchedulerFactory是是实现类,基于Quartz的配置文件创建Scheduler调度程序
- Job是在未来时间内能被任务调度的时间类(上面Job就是这个东西啦~)
-
上面的代码 job是由JobFactor来创建的,你可以看见它的导入包为org.quartz.JobBuilder.*。 JobDetail job = newJob(XXXJob.class) .withIdentity("job1", "group1") .build(); 在创建JobDetail的时候,将要执行的job的类名传给了JobDetail, 所以scheduler就知道了要执行何种类型的job,然后每次当scheduler执行job时, 在调用其execute(…)方法之前会创建该类的一个新的实例, 执行完毕,对该实例的引用就被丢弃了,实例会被垃圾回收。 当默认使用JobFactory的时候,Job是一定要有一个默认的构造函数的, 并且其类不定义有状态的数据属性,因为数据会在执行的时候不会保留.
JobDetail是用来定义定时任务的实例(为newJob()~~)
-
JobBuilder 是用于声明一个任务实例,也可以定义关于该任务的详情比如任务名、组名等,这个声明的实例将会作为一个实际执行的任务。
JobDetail实例是通过JobBuilder类创建。 导入该类下的所有静态方法: import static org.quartz.JobBuilder.*; JobDetail的对象的一部分JobDataMap,可以用来增加属性和配置,在Job实例多次被执行的时候跟踪Job JobDataMap可以包含不限量的(序列化)数据对象,在job执行实例的时候,可以使用其中的数据。 JobDetail job = newJob(DumbJob.class) .withIdentity("myJob", "group1") // name "myJob", group "group1" .usingJobData("jobSays", "Hello World!") .usingJobData("myFloatValue", 3.141f) .build(); public class DumbJob implements Job { public DumbJob() { } public void execute(JobExecutionContext context) throws JobExecutionException { JobKey key = context.getJobDetail().getKey(); JobDataMap dataMap = context.getJobDetail().getJobDataMap(); String jobSays = dataMap.getString("jobSays"); float myFloatValue = dataMap.getFloat("myFloatValue"); System.err.println("Instance " + key + " of DumbJob says: " + jobSays + ", and val is: " + myFloatValue); } } 使用JobStory持久化的存储机制是对JobDataMap不友好的,原因是这会将JobDataMap存储的对象序列化, 导致类的版本不一致的问题,解决办法是在JobStory或者JobDataMap配置Map仅允许存储基本类型和string类型, 避免后续的序列化问题。
-
TriggerBuilder 是触发器创建器,用于创建触发器trigger实例。
-
Trigger是触发器,表示任务啥时候执行。定义需要执行的任务在那个时间被执行的时间条件。
从导入的包 import static org.quartz.TriggerBuilder.*; import static org.quartz.SimpleScheduleBuilder.*; import static org.quartz.DateBuilder.*: 可以发现: SimpleTrigger实例通过TriggerBuilder设置主要的属性, 通过SimpleScheduleBuilder设置与SimpleTrigger相关的属性, DataBuilder使用它可以非常方便地构造基于开始时间(或终止时间)的调度策略。 上面使用了StartNow() 也可以使用StartAt() 指定执行的时间 trigger = newTrigger() .withIdentity("trigger3", "group1") .startAt(myTimeToStartFiring) .withSchedule(simpleSchedule() .withIntervalInSeconds(10) .withRepeatCount(10)) .forJob(myJob) // 如果开始时间没有给定就会使用当前时间 // 十次重发将会产生十一次触发 // 识别 job 是JobDetail的.build()
四、附录
CronTrigger的表达式常用示例: