1、单机任务调度,任务可能会重叠并发执行。
如示例:每隔10秒执行此任务,但是任务执行耗时20s
执行结果(任务重叠执行):
解决:单机可加注解DisallowConcurrentExecution解决,集群环境必须靠分布式如quartz集群方案解决,如果保证不了任务的重叠执行,可以用分布式锁或任务执行幂等性来保证。
示例:使用注解DisallowConcurrentExecution解决效果:
执行结果:
2、使用注解DisallowConcurrentExecution来解决任务重叠问题,可能由于任务执行时间长,导致任务调度不准确。
示例:
执行结果(任务调度周期与耗时相关,不再准确---每隔10s执行,耗时20s):
如果运行任务重叠执行,则(任务调度周期为准-每隔10s执行):
产生1、2现象的原因在于获取可被调度的任务时(默认内存存储任务job及调度信息):
代码语言:javascript复制org.quartz.simpl.RAMJobStore#acquireNextTriggers
当获取任务的触发器时,同时也会删除其存储信息:
在任务被真正调度之前,根据是否可以重叠执行,如果可以重叠执行,则重新把触发器添加存储起来,下次任务调度轮询可以被再次调度:
代码语言:javascript复制org.quartz.simpl.RAMJobStore#triggersFired
当任务被执行完时,不能重叠执行的任务也会被重新存储起来,下次任务调度轮询可以被再次调度:
代码语言:javascript复制org.quartz.simpl.RAMJobStore#triggeredJobComplete
3、任务会因为任务线程池没有空闲线程执行,导致调度线程等待,任务不会再被调度。
任务调度线程获取的任务会交给工作线程池去调度,默认是固定大小的线程池SimpleThreadPool,如果线程池没有空闲线程执行任务,则调度线程会一直等待:
代码语言:javascript复制org.quartz.core.QuartzSchedulerThread#run
代码语言:javascript复制org.quartz.simpl.SimpleThreadPool#blockForAvailableThreads
实现:
4、如果任务需要停止继续被调度,可以抛出异常JobExecutionException,设置信息:
根据异常信息转换为CompletedExecutionInstruction:
根据CompletedExecutionInstruction判断任务是否能被继续调度:
代码语言:javascript复制org.quartz.simpl.RAMJobStore#triggeredJobComplete
附:
代码语言:javascript复制package com.renzhikeji.demo;
import org.quartz.*;
import org.quartz.impl.StdSchedulerFactory;
import java.util.concurrent.TimeUnit;
public class QuartzDemo {
public static void main(String[] args) throws SchedulerException, InterruptedException {
//创建调度器
Scheduler scheduler = StdSchedulerFactory.getDefaultScheduler();
//创建触发器
Trigger trigger = TriggerBuilder.newTrigger().withSchedule(CronScheduleBuilder.cronSchedule("0/10 * * * * ?"))
.build();
//定义一个job
JobDetail job = JobBuilder.newJob(MyJob.class).withIdentity("MyJob", "MyJob_Group")
.usingJobData("author", "认知科技技术团队").build();
//使用jobDateMap
job.getJobDataMap().put("微信公众号", "认知科技技术团队");
//启动
scheduler.start();
//调度假如这个job
scheduler.scheduleJob(job, trigger);
TimeUnit.HOURS.sleep(1);
scheduler.shutdown(true);
}
public static class MyJob implements Job {
@Override
public void execute(JobExecutionContext jobExecutionContext) {
System.out.println("hello begin " Thread.currentThread().getName());
try {
TimeUnit.SECONDS.sleep(20);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
System.out.println("hello end " Thread.currentThread().getName());
}
}
}
1、Java避坑指南:ScheduledThreadPoolExecutor避坑
2、Java避坑指南:ScheduledThreadPoolExecutor避坑之异常信息会丢失,任务不再继续被调度的源码分析