Quartz定时任务运行时,能够否对某个任务重新调度呢?

背景

quartz 是一个功能丰富、开源、分布式的任务调用框架,我参与的很多项目都用它来实现定时调度功能。关于定时任务,有一个常见的需求是:由 Web 应用来控制定时任务的启动、停止、调度周期等。

本文探讨的是,对于当前正在调度的、耗时较长的任务 ,如果通过 Scheduler 类的 pauseJob、deleteJob、rescheduleJob 方法重新对该任务调度,是否会立即生效呢?

此时,当前这一轮 Job 类的 execute 方法会立即终止呢,还是等待这一轮任务完成后,任务新的调度操作才会生效,一起来看看。

任务调度 API

任务管理的停止、重启、重新调度,分别对应三个方法。

ini 复制代码
    public static void stopJob(Scheduler scheduler) {
        try {
	        JobKey jobKey = JobKey.jobKey("testJobId","testJobGroup");
            scheduler.pauseJob(jobKey);
            logger.info("Stop test job ok.");
        } catch (SchedulerException e) {
            logger.error("停止定时任务异常!",e);
        }
    }

public static void resumeJob(Scheduler scheduler) {
        try {
	        JobKey jobKey = JobKey.jobKey("testJobId","testJobGroup");
            scheduler.resumeJob(jobKey);
            logger.info("Resume test job ok.");
        } catch (SchedulerException e) {
            logger.error("停止定时任务异常!",e);
        }
    }

public static void rescheduleJob(Scheduler scheduler) {
       TriggerKey triggerKey = TriggerKey.triggerKey("testJobId","testJobGroup" );
		try {
		    CronTrigger trigger = (CronTrigger) scheduler.getTrigger(triggerKey);
		    CronScheduleBuilder scheduleBuilder = CronScheduleBuilder.cronSchedule(cornExp);
		
		    //按新的cronExpression表达式重新构建trigger
		    trigger = trigger.getTriggerBuilder().withIdentity(triggerKey).withSchedule(scheduleBuilder).build();
		    Date rescheduleJob = scheduler.rescheduleJob(triggerKey, trigger);
		    SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss SSS");
		    JobKey jobKey = JobKey.jobKey("testJobId", "testJobGroup");
		    logger.info(jobKey.getName() + " 已被重新安排执行于: " + sdf.format(rescheduleJob) + ",并且以如下规则重复执行: " + trigger.getCronExpression());
		} catch (SchedulerException e) {
		    e.printStackTrace();
		}
    }

Job 的 execute 模拟长任务

定义一个由 @DisallowConcurrentExecution 注解的、非并行任务类,用 sleep 模拟长耗时的逻辑:

java 复制代码
@DisallowConcurrentExecution
@Component
public class VulnerCrawlerJob implements Job {
    @Override
    public void execute(JobExecutionContext jobExecutionContext) 
    throws JobExecutionException {
        logger.info("Start A  job  at "+new Date());
        if(true) {
            try {
                Thread.sleep(300000);
                logger.info("Finished A  job  at "+new Date());
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            return;
        }
    }
}

测试结果

任务调度通过页面控制,状态流转过程为:

  1. 应用启动,且到达调度时间点后,任务开始执行;
  2. 任务执行休眠 5分钟内,执行 stop 停止任务;
  3. 修改调度周期,再执行 rescheduleJob;
  4. 修改系统时间,使其为下一轮调度时间之前。

第一步,在任务启动且休眠的5分钟内,通过页面停止任务:

第二步,再开启任务,并修改执行周期: 第三步,修改系统时间为 2020-12-6 日 11:12 分,看看下一轮任务的执行周期是否发生变化。

查看日志结果: 序号②、③、④ 说明,11:13 分开始的任务执行期间,虽然重新调度了任务在 11:23 分执行,但是上一轮任务仍然继续,且在 11:18 分执行结束。

修改时间为 12月6日 11:11 分后,新一轮任务在 11:23 分执行,按新的周期。

启示录

反复验证后,可以得到这个结论:对于当前正在调度的、耗时较长的任务 ,如果通过 Scheduler 类的 pauseJob、deleteJob、rescheduleJob 方法重新对该任务角度,新的调度策略对本轮任务无效。

那么问题来了,怎么才能干预当前正在执行的 Job,使其可以即使终止呢?有一种思路是实现 InterruptableJob ,execute 执行过程中轮询中断标识。但是,对于没有循环逻辑、只有大量串行计算的任务来说,还是没有效。

相关推荐
Cc.Y23 分钟前
Java零基础入门:类与对象深度掌握 —— 从“零散变量“到“打包管理“
java·开发语言·python
\光辉岁月/24 分钟前
2.java变量
java·开发语言·变量
Wang's Blog27 分钟前
Java 项目实战: 外卖平台优化-前端dist部署与Nginx反向代理rewrite
java·前端·nginx
专业程序开发源28 分钟前
springboot生命故事书制作小程序65290-计算机课程设计、毕业设计
java·vue.js·spring boot·后端·mysql·小程序·课程设计
代码山河42 分钟前
Java的前世今生:从Oak语言到Java 23的发展史
java·学习·架构·教程·面向对象·项目
vx_Biye_Design44 分钟前
springboot角色扮演服务平台65161-计算机课程设计、毕业设计
java·vue.js·spring boot·后端·python·spring·课程设计
李游Leo1 小时前
HarmonyOS 7 QuickDock 闪控窗开发实录 02:floatView × floatingBall:形态切换、位置恢复与单一状态源【鸿蒙心迹】
java·华为·harmonyos
企业数字化笔记1 小时前
视频局部合成怎么做得自然?掩膜修复、边缘羽化、时序稳定与背景替换
java·javascript·音视频
仍然.1 小时前
Redis---主从复制
java·数据库·redis
vx_Biye_Design1 小时前
springboot咖啡厅顾客点单管理系统12080-计算机课程设计、毕业设计
java·vue.js·spring boot·后端·spring·课程设计·express