任务调度:调度器、任务、触发器
任务
Job
- 拥有JobDataMap属性,保存信息
- 每次执行execute方法前都会创建一个新的Job 实例,执行完后会被丢弃回收
@PersistJobDataAfterExecution 持久化JobDataMap
@DisallowConcurrentExecution 不允许并发执行
RequestsRecovery 系统崩溃后,可恢复执行
Durability 非持久的话,当没有活跃Trigger与其绑定时,job会被自动删除
/*
* 实现此接口定义需要执行的任务
* 实现类必须包含一个公共的无参构造器
* JobDataMap保存了Job 运行时的信息
*/
public interface Job {
// JobExecutionContext 提供调度上下文信息
void execute(JobExecutionContext context)throws JobExecutionException;
}
JobDetail
/*
* 用来描述Job实例的静态信息,如Job名称、描述、关联监听器等信息
* 通过 Job名(name)和组名(group)定位唯一的Job实例
*/
public interface JobDetail {
public JobKey getKey();
public String getDescription();
public Class<? extends Job> getJobClass();
public JobDataMap getJobDataMap();
}
JobBuilder
/*
* 用来创建JobDetail
*/
public class JobBuilder {
public JobDetail build() {
JobDetailImpl job = new JobDetailImpl();
job.setJobClass(jobClass);
job.setDescription(description);
if(key == null)
key = new JobKey(Key.createUniqueName(null), null);
job.setKey(key);
job.setDurability(durability);
job.setRequestsRecovery(shouldRecover);
if(!jobDataMap.isEmpty())
job.setJobDataMap(jobDataMap);
return job;
}
}
JobDetail job = newJob(MyJob.class)
.withIdentity("myJob")
.build();
触发器
Trigger
- 拥有JobDataMap属性,保存信息,可用于传参数给Job
- 不同Trigger之间相互独立,两个不同Trigger同时触发一个Job时,会执行两次

/*
* 描述触发Job执行的时间规则
* SimpleTriger、CronTrigger
*/
public interface Trigger {
public TriggerKey getKey();
public JobKey getJobKey();
public JobDataMap getJobDataMap();
}
SimpleTrigger
- 以固定时间间隔重复执行
- endTime到了指定后,即使设置的 repeatCount大于零,trigger也会终止。
startTime ,endTime, repeat count, repeat interval
public interface SimpleTrigger extends Trigger {
public int getRepeatCount();
public long getRepeatInterval();
public int getTimesTriggered();
}
Trigger trigger = TriggerBuilder.newTrigger()
.withIdentity("trigger1", "group1")
//开始触发时间,现在设置为程序启动后3秒
.startAt(DateTime.now().plusSeconds(3).toDate())
//设置重复时间间隔interval、重复次数repeatCount
.withSchedule(SimpleScheduleBuilder.repeatSecondlyForever(3))
.build();
CronTrigger
public interface CronTrigger extends Trigger {
public String getCronExpression();
public String getExpressionSummary();
TriggerBuilder<CronTrigger> getTriggerBuilder();
}
Trigger trigger = TriggerBuilder.newTrigger()
.withIdentity("trigger1", "group1")
// cron 表达式
.withSchedule(CronScheduleBuilder.dailyAtHourAndMinute(15,0))
//.withSchedule(cronSchedule("0/20 * * * * ?"))
.build();
TriggerBuilder
public class TriggerBuilder<T extends Trigger> {
private TriggerKey key;
private String description;
private Date startTime = new Date();
private Date endTime;
private int priority = Trigger.DEFAULT_PRIORITY;
private String calendarName;
private JobKey jobKey;
private JobDataMap jobDataMap = new JobDataMap();
public static TriggerBuilder<Trigger> newTrigger() {
return new TriggerBuilder<Trigger>();
}
public TriggerBuilder<T> withIdentity(String name, String group) {
key = new TriggerKey(name, group);
return this;
}
public TriggerBuilder<T> startAt(Date triggerStartTime) {
this.startTime = triggerStartTime;
return this;
}
public TriggerBuilder<T> endAt(Date triggerEndTime) {
this.endTime = triggerEndTime;
return this;
}
public <SBT extends T> TriggerBuilder<SBT> withSchedule(ScheduleBuilder<SBT> schedBuilder) {
this.scheduleBuilder = schedBuilder;
return (TriggerBuilder<SBT>) this;
}
public TriggerBuilder<T> forJob(JobKey keyOfJobToFire) {
this.jobKey = keyOfJobToFire;
return this;
}
public TriggerBuilder<T> usingJobData(JobDataMap newJobDataMap) {
// add any existing data to this new map
for(String dataKey: jobDataMap.keySet()) {
newJobDataMap.put(dataKey, jobDataMap.get(dataKey));
}
jobDataMap = newJobDataMap; // set new map as the map to use
return this;
}
newTrigger()
.withIdentity("trigger3", "group1")
.startAt(myTimeToStartFiring) // if a start time is not given (if this line were omitted), "now" is implied
.withSchedule(simpleSchedule()
.withIntervalInSeconds(10)
.withRepeatCount(10)) // note that 10 repeats will give a total of 11 firings
// 绑定到具体jobDetail
.forJob(myJob) // identify job with handle to its JobDetail itself .endAt(dateOf(22, 0, 0))
.build();
调度器
Scheduler
- 代表一个Quartz的独立运行容器
- 将Trigger绑定到某一JobDetail,当Trigger被触发时,对应的Job被执行
- 一个Scheduler可以拥有多个Trigger和多个Job
- 一个Job可以对应多个Trigger,但一个Trigger只能对应一个Job
- scheduler启动后仍可注册任务和trigger
public interface Scheduler {
String getSchedulerName() throws SchedulerException;
String getSchedulerInstanceId() throws SchedulerException;
//获取Scheduler实例的上下文信息
SchedulerContext getContext() throws SchedulerException;
void start() throws SchedulerException;
void standby() throws SchedulerException;
void shutdown() throws SchedulerException;
List<JobExecutionContext> getCurrentlyExecutingJobs()
throws SchedulerException;
//将jobDetail和trigger注册到Scheduler
//用trigger对jobDetail进行调度
Date scheduleJob(JobDetail jobDetail, Trigger trigger)
throws SchedulerException;
// 先注册jobDetail,后注册指定了关联jobDetail的Trigger
void addJob(JobDetail jobDetail, boolean replace)
throws SchedulerException;
//trigger必须绑定jobDetail
Date scheduleJob(Trigger trigger)
throws SchedulerException;
boolean unscheduleJob(TriggerKey triggerKey)
throws SchedulerException;
Date rescheduleJob(TriggerKey triggerKey, Trigger newTrigger)
throws SchedulerException;
boolean deleteJob(JobKey jobKey)
throws SchedulerException;
void triggerJob(JobKey jobKey)
throws SchedulerException;
void pauseJob(JobKey jobKey)
throws SchedulerException;
void pauseTrigger(TriggerKey triggerKey)
throws SchedulerException;
void resumeJob(JobKey jobKey)
throws SchedulerException;
void resumeTrigger(TriggerKey triggerKey)
throws SchedulerException;
List<? extends Trigger> getTriggersOfJob(JobKey jobKey)
throws SchedulerException;
JobDetail getJobDetail(JobKey jobKey)
throws SchedulerException;
Trigger getTrigger(TriggerKey triggerKey)
throws SchedulerException;
}
public class StdScheduler implements Scheduler {
}
QuartzScheduler
Scheduler的间接实现,由StdScheduler代理或适配
public class QuartzScheduler implements RemotableQuartzScheduler {
public Date scheduleJob(JobDetail jobDetail, Trigger trigger) throws SchedulerException {
// 检查调度器是否开启,如果关闭则throw异常到上层 validateState(); ......
// 获取trigger首次触发job的时间,以此时间为起点,每隔一段指定的时间触发job
Date ft = trig.computeFirstFireTime(cal);
if (ft == null) {
throw new SchedulerException( "Based on configured schedule, the given trigger '" + trigger.getKey() + "' will never fire.");
}
// 把job和trigger注册进调度器的jobStore resources.getJobStore().storeJobAndTrigger(jobDetail, trig);
// 通知job监听者
notifySchedulerListenersJobAdded(jobDetail);
// 通知调度器线程 notifySchedulerThread(trigger.getNextFireTime().getTime());
// 通知trigger监听者
notifySchedulerListenersSchduled(trigger);
return ft;
}
}
public Scheduler getScheduler() throws SchedulerException {
// 读取quartz配置文件,未指定则顺序遍历各个path下的quartz.properties文件
// 解析出quartz配置内容和环境变量,存入PropertiesParser对象
// PropertiesParser组合了Properties(继承Hashtable),定义了一系列对Properties的操作方法,比如getPropertyGroup()批量获取相同前缀的配置。配置内容和环境变量存放在Properties成员变量中
if (cfg == null) {
initialize();
}
// 获取调度器池,采用了单例模式
// 其实,调度器池的核心变量就是一个hashmap,每个元素key是scheduler名,value是scheduler实例
// getInstance()用synchronized防止并发创建
SchedulerRepository schedRep = SchedulerRepository.getInstance();
// 从调度器池中取出当前配置所用的调度器
Scheduler sched = schedRep.lookup(getSchedulerName());
。。。。。。
// 如果调度器池中没有当前配置的调度器,则实例化一个调度器,主要动作包括:
// 1)初始化threadPool(线程池):开发者可以通过org.quartz.threadPool.class配置指定使用哪个线程池类,比如SimpleThreadPool。先class load线程池类,接着动态生成线程池实例bean,然后通过反射,使用setXXX()方法将以org.quartz.threadPool开头的配置内容赋值给bean成员变量;
// 2)初始化jobStore(任务存储方式):开发者可以通过org.quartz.jobStore.class配置指定使用哪个任务存储类,比如RAMJobStore。先class load任务存储类,接着动态生成实例bean,然后通过反射,使用setXXX()方法将以org.quartz.jobStore开头的配置内容赋值给bean成员变量;
// 3)初始化dataSource(数据源):开发者可以通过org.quartz.dataSource配置指定数据源详情,比如哪个数据库、账号、密码等。jobStore要指定为JDBCJobStore,dataSource才会有效;
// 4)初始化其他配置:包括SchedulerPlugins、JobListeners、TriggerListeners等;
// 5)初始化threadExecutor(线程执行器):默认为DefaultThreadExecutor;
// 6)创建工作线程:根据配置创建N个工作thread,执行start()启动thread,并将N个thread顺序add进threadPool实例的空闲线程列表availWorkers中;
// 7)创建调度器线程:创建QuartzSchedulerThread实例,并通过threadExecutor.execute(实例)启动调度器线程;
// 8)创建调度器:创建StdScheduler实例,将上面所有配置和引用组合进实例中,并将实例存入调度器池中
sched = instantiate();
return sched;
}
SchedulerFactory
用于创建Scheduler实例
public interface SchedulerFactory {
Scheduler getScheduler() throws SchedulerException;
Scheduler getScheduler(String schedName) throws SchedulerException;
Collection<Scheduler> getAllSchedulers() throws SchedulerException;
}
JobDataMap
jobDataMap 存储状态实例
- 必须加注解 @PersistJobDataAfterExecution,才能存储相关信息供以后触发使用
@PersistJobDataAfterExecution
@DisallowConcurrentExecution
public class ColorJob implements Job {
public static final String FAVORITE_COLOR = "favorite color";
public static final String EXECUTION_COUNT = "count";
public ColorJob() {
}
public void execute(JobExecutionContext context)
throws JobExecutionException {
JobKey jobKey = context.getJobDetail().getKey();
JobDataMap data = context.getJobDetail().getJobDataMap();
String favoriteColor = data.getString(FAVORITE_COLOR);
int count = data.getInt(EXECUTION_COUNT);
count++;
data.put(EXECUTION_COUNT, count);
}
}
JobDetail job2 = newJob(ColorJob.class)
.withIdentity("job2", "group1").build();
SimpleTrigger trigger2 = newTrigger()
.withIdentity("trigger2", "group1")
.startAt(startTime)
.withSchedule(simpleSchedule().withIntervalInSeconds(3).withRepeatCount(4))
.build();
// pass initialization parameters into the job
// this job has a different favorite color!
job2.getJobDataMap().put(ColorJob.FAVORITE_COLOR, "Red");
job2.getJobDataMap().put(ColorJob.EXECUTION_COUNT, 1);
Listener
TriggerListener
public interface TriggerListener {
public String getName();
public void triggerFired(Trigger trigger, JobExecutionContext context);
public boolean vetoJobExecution(Trigger trigger, JobExecutionContext context);
public void triggerMisfired(Trigger trigger);
public void triggerComplete(Trigger trigger, JobExecutionContext context,
int triggerInstructionCode);
}
public abstract class TriggerListenerSupport implements TriggerListener {
}
JobListener
public interface JobListener {
public String getName();
public void jobToBeExecuted(JobExecutionContext context);
public void jobExecutionVetoed(JobExecutionContext context);
public void jobWasExecuted(JobExecutionContext context,
JobExecutionException jobException);
}
public abstract class JobListenerSupport implements JobListener {
}
JobListener listener = new Job1Listener();
Matcher<JobKey> matcher = KeyMatcher.keyEquals(job.getKey());
sched.getListenerManager().addJobListener(listener, matcher);
SchedulerListener
public interface SchedulerListener {
public void jobScheduled(Trigger trigger);
public void jobUnscheduled(String triggerName, String triggerGroup);
public void triggerFinalized(Trigger trigger);
public void triggersPaused(String triggerName, String triggerGroup);
public void triggersResumed(String triggerName, String triggerGroup);
public void jobsPaused(String jobName, String jobGroup);
public void jobsResumed(String jobName, String jobGroup);
public void schedulerError(String msg, SchedulerException cause);
public void schedulerStarted();
public void schedulerInStandbyMode();
public void schedulerShutdown();
public void schedulingDataCleared();
}