一图理解quartz任务调度及注意事项

2023-11-20 10:07:25 浏览数 (1)


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避坑之异常信息会丢失,任务不再继续被调度的源码分析

0 人点赞