超详细 任务调度框架Quartz用法指南( 五 )

启动程序后可以看到,调度器已经启动:
2021-10-06 16:26:05.162INFO 10764 --- [shivaScheduler]] o.s.s.quartz.SchedulerFactoryBean: Starting Quartz Scheduler now, after delay of 1 seconds2021-10-06 16:26:05.306INFO 10764 --- [shivaScheduler]] org.quartz.core.QuartzScheduler: Scheduler shivaScheduler_$_DESKTOP-OKMJ1351633508761366 started.新增调度任务添加任务,使用如下 json 进行请求:
{"concurrent": "1","cronExpression": "0/10 * * * * ?","invokeTarget": "mysqlJob.execute('got it!!!')","jobGroup": "mysqlGroup","jobId": 9,"jobName": "新增 mysqlJob 任务","misfirePolicy": "1","remark": "","status": "0"}@Override@Transactional(rollbackFor = Exception.class)public int insertJob(QuartzJob job) throws Exception {// 先将任务设置为暂停状态job.setStatus(ScheduleConstants.Status.PAUSE.getValue());int rows = quartzMapper.insert(job);if (rows > 0) {ScheduleUtils.createScheduleJob(scheduler, job);}return rows;}先将任务设置为暂停状态,数据库插入成功后,在调度器创建任务 。
再手动启动任务,根据 ID 来启动任务:

超详细 任务调度框架Quartz用法指南

文章插图
 
实现代码:
@Overridepublic int changeStatus(Long jobId, String status) throws SchedulerException {int rows = quartzMapper.changeStatus(jobId, status);if (rows == 0) {return rows;}//更新成功,需要改调度器内任务的状态//拿到整个任务QuartzJob job = quartzMapper.selectJobById(jobId);//根据状态来启动或者关闭if (ScheduleConstants.Status.NORMAL.getValue().equals(status)) {rows = resumeJob(job);} else if (ScheduleConstants.Status.PAUSE.getValue().equals(status)) {rows = pauseJob(job);}return rows;}@Overridepublic int resumeJob(QuartzJob job) throws SchedulerException {Long jobId = job.getJobId();String jobGroup = job.getJobGroup();job.setStatus(ScheduleConstants.Status.NORMAL.getValue());int rows = quartzMapper.updateById(job);if (rows > 0) {scheduler.resumeJob(ScheduleUtils.getJobKey(jobId, jobGroup));}return rows;}暂停任务的代码也相同 。
调用启动后可以看到控制台打印日志:
2021-10-06 20:36:30.018INFO 8536 --- [eduler_Worker-3] cn.shiva.quartz.job.MysqlJob: 执行 Mysql Job,当前时间:2021-10-06 20:36:30,任务参数:got it!!!2021-10-06 20:36:40.016INFO 8536 --- [eduler_Worker-4] cn.shiva.quartz.job.MysqlJob: 执行 Mysql Job,当前时间:2021-10-06 20:36:40,任务参数:got it!!!2021-10-06 20:36:50.017INFO 8536 --- [eduler_Worker-5] cn.shiva.quartz.job.MysqlJob: 执行 Mysql Job,当前时间:2021-10-06 20:36:50,任务参数:got it!!!如果涉及到任务修改,需要在调度器先删除原有任务,重新创建调度任务 。
启动初始化任务这部分倒是比较简单,初始化的时候清空原有任务,重新创建就好了:
/*** 项目启动时,初始化定时器 主要是防止手动修改数据库导致未同步到定时任务处理(注:不能手动修改数据库ID和任务组名,否则会导致脏数据)*/@PostConstructpublic void init() throws Exception {scheduler.clear();List<QuartzJob> jobList = quartzMapper.selectJobAll();for (QuartzJob job : jobList) {ScheduleUtils.createScheduleJob(scheduler, job);}}其他说明并发执行上面有并发和非并发的区别,通过 @DisallowConcurrentExecution 注解来实现阻止并发 。
Quartz定时任务默认都是并发执行的,不会等待上一次任务执行完毕,只要间隔时间到就会执行, 如果定时任执行太长,会长时间占用资源,导致其它任务堵塞 。
@DisallowConcurrentExecution 禁止并发执行多个相同定义的JobDetail, 这个注解是加在Job类上的, 但意思并不是不能同时执行多个Job, 而是不能并发执行同一个Job Definition(由JobDetail定义), 但是可以同时执行多个不同的JobDetail 。
举例说明,我们有一个Job类,叫做SayHelloJob, 并在这个Job上加了这个注解, 然后在这个Job上定义了很多个JobDetail, 如sayHelloToJoeJobDetail, sayHelloToMikeJobDetail, 那么当scheduler启动时, 不会并发执行多个sayHelloToJoeJobDetail或者sayHelloToMikeJobDetail, 但可以同时执行sayHelloToJoeJobDetail跟sayHelloToMikeJobDetail
@PersistJobDataAfterExecution 同样, 也是加在Job上 。表示当正常执行完Job后, JobDataMap中的数据应该被改动, 以被下一次调用时用 。
当使用 @PersistJobDataAfterExecution 注解时, 为了避免并发时, 存储数据造成混乱, 强烈建议把 @DisallowConcurrentExecution 注解也加上 。


推荐阅读