Java 定时任务quartz

quartz定时任务

一、添加Maven依赖

    <!-- 定时任务 -->
    <dependency>
      <groupId>org.quartz-scheduler</groupId>
      <artifactId>quartz</artifactId>
      <version>2.3.0</version>
    </dependency>

二、创建执行任务的任务类

如下所示:创建了TestJob01并实现了Job,该任务只是简单的打印当前时间

package cn.wh.job;

import org.quartz.Job;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;

import java.text.SimpleDateFormat;
import java.util.Date;

/**
 * 定时执行任务的任务类-测试
 * @author wanghao
 *
 */
public class TestJob01 implements Job {
    @Override
    public void execute(JobExecutionContext jobExecutionContext) throws JobExecutionException {
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss");
        System.out.println("任务打印"+sdf.format(new Date()));
    }
}

三、创建调用任务的调度类

 

package cn.wh.scheduler;

import cn.wh.job.TestJob01;
import org.quartz.*;
import org.quartz.impl.StdSchedulerFactory;

/**
 * 任务调度类
 */
public class TestJob01Scheduler {

    //创建调度器
    public static Scheduler getScheduler() throws SchedulerException {
        SchedulerFactory schedulerFactory = new StdSchedulerFactory();
        return schedulerFactory.getScheduler();
    }

    // 执行任务
    public static void run() throws SchedulerException{
        //创建任务
        JobDetail jobDetail = JobBuilder.newJob(TestJob01.class).withIdentity("testJob01", "group11").build();
        //创建触发器 每5秒钟执行一次
        Trigger trigger = TriggerBuilder.newTrigger().withIdentity("trigger1", "group21")
                .withSchedule(SimpleScheduleBuilder.simpleSchedule().withIntervalInSeconds(5).repeatForever())
                .build();
        Scheduler scheduler = getScheduler();
        //将任务及其触发器放入调度器
        scheduler.scheduleJob(jobDetail, trigger);
        //调度器开始调度任务
        // 启动
        if (!scheduler.isShutdown()) {
            scheduler.start();
        }
    }

    public static void main(String[] args) throws SchedulerException {
        TestJob01Scheduler testJob01Scheduler = new TestJob01Scheduler();
        testJob01Scheduler.run();
    }
}

执行结果如下图所示:每隔五秒执行一次

 执行过程如下图:


使用Cron表达式,执行定时任务

Cron表达式包含6个必要组件和一个可选组件,如下表所示。

 

位置

含义

允许的特殊字符

1

秒(0~59)

, -  *  /

2

分(0~59)

, -  *  /

3

小时(0~24)

, -  *  /

4

日期(1~31)

, -  *  /  ?  L  W  C

5

月(JAN~DEC或1~12)

, -  *  /

6

星期(SUN~SAT或1~7)

, -  *  /  ?  L  C  #

7

年(可选,1970~2099),若为空,表示全部时间范围

, -  *  /

 
特殊字符的含义,见下表。
 

特殊字符

说明

*

通配符,任意值

?

无特定值。通常和其他指定的值一起使用,表示必须显示该值但不能检查

-

范围。e.g.小时部分10-12表示10:00,11:00, 12:00

,

列分隔符。可以让你指定一系列的值。e.g.在星期域中指定MON、TUE和WED

/

增量。表示一个值的增量,e.g.分钟域中0/1表示从0开始,每次增加1min

L

表示Last。它在日期和星期域中表示有所不同。在日期域中,表示这个月的最后一天,而在星期域中,它永远是7(星期六)。当你希望使用星期中某一天时,L字符非常有用。e.g.星期域中6L表示每一个月的最后一个星期五

W

在本月内离当天最近的工作日触发,所谓的最近工作日,即当天到工作日的前后最短距离,如果当天即为工作日,则距离是0;所谓本月内指的是不能跨月取到最近工作日,即使前/后月份的最后一天/第一天确实满足最近工作日。e.g. LW表示本月的最后一个工作日触发,W强烈依赖月份。

#

表示该月的第几个星期,e.g. 1#2表示每一个月的第一个星期一

C

日历值。日期值是根据一个给定的日历计算出来的。在日期域中给定一个20C将在20日(日历包括20日)或20日后日历中包含的第一天(不包括20日)激活触发器。例如在一个星期域中使用6C表示日历中星期五(日历包括星期五)或者第一天(日历不包括星期五)

 

代码如下:

package cn.wh.utils;

import cn.wh.job.TestJob01;
import org.quartz.*;
import org.quartz.impl.StdSchedulerFactory;

public class QuartzManager {

    private static SchedulerFactory gSchedulerFactory = new StdSchedulerFactory();
    private static String JOB_GROUP_NAME = "EXTJWEB_JOBGROUP_NAME";
    private static String TRIGGER_GROUP_NAME = "EXTJWEB_TRIGGERGROUP_NAME";

    /**
     * 添加一个定时任务,使用默认的任务组名,触发器名,触发器组名
     * @param jobName 任务名
     * @param cls 任务
     * @param time 时间设置
     */

    public static void addJob(String jobName, Class cls, String time) {
        try {
            // org.quartz-scheduler
            Scheduler sched = gSchedulerFactory.getScheduler();
            // 任务名,任务组,任务执行类
            //创建任务
            JobDetail jobDetail = JobBuilder.newJob(cls).withIdentity(jobName, TRIGGER_GROUP_NAME).build();
            //可以传递参数
            jobDetail.getJobDataMap().put("param", "railsboy");
            // 触发器
            //每秒钟触发一次任务
            CronTrigger trigger = TriggerBuilder.newTrigger().
                    withIdentity(jobName, TRIGGER_GROUP_NAME).
                    withSchedule(CronScheduleBuilder.cronSchedule("*/3 * * * * ? *")).
                    build();
            // 触发器时间设定
            sched.scheduleJob(jobDetail, trigger);
            // 启动
            if (!sched.isShutdown()) {
                sched.start();
            }
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    public static void main(String[] args) {
        QuartzManager.addJob("Test01",TestJob01.class,"");
    }
}

可以看到任务每三秒执行一次。

 

五、实现动态定时任务

5.1 定时任务管理类

定时任务管理类包含调度器初始化、新增/更新任务、停止任务、执行一次任务

5.1.1 调度器初始化

默认调度器线程10个

public class QuartzDynamicTaskManager {
public static final String JOB_KEYNAME_PREFIX = "JOB_"; public static final String TRIGGER_KEYNAME_PREFIX = "TRIGGER_"; public static final String JOB_COMMON_GROUP = "DEFAULT_GROUP"; // Quartz调度器 private final Scheduler scheduler; public QuartzDynamicTaskManager() throws SchedulerException { // 初始化调度器 StdSchedulerFactory factory = new StdSchedulerFactory(); this.scheduler = factory.getScheduler(); // 获取元数据,查看线程池大小 int threadPoolSize = scheduler.getMetaData().getThreadPoolSize(); log.info("Quartz默认线程池默认大小:{}",threadPoolSize); scheduler.start(); } }

5.1.2 新增/更新任务/恢复任务

新增任务和更新任务可以分开也可以合到一个方法如下:

使用了泛型进行通用的定时任务管理

    /**
     * 新增/更新定时任务
     * @param taskId 任务ID(作为JobKey和TriggerKey的标识)
     * @param jobGroup 任务组
     * @param cron Cron表达式
     * @param param 任务参数
     * @throws SchedulerException
     * @throws ParseException
     */
    public void addOrUpdateTask(String taskId,String jobGroup, String cron, String param,Class<? extends Job> clz) throws SchedulerException, ParseException {
        JobKey jobKey = new JobKey(taskId, jobGroup);
        TriggerKey triggerKey = new TriggerKey(TRIGGER_KEYNAME_PREFIX+ taskId, jobGroup);
        JobDetail jobDetail = scheduler.getJobDetail(jobKey);
        if (jobDetail == null) {
            jobDetail = JobBuilder.newJob(clz)
                    .withIdentity(jobKey)
                    .usingJobData("param", param) // 设置任务参数
                    .storeDurably(true) // 即使没有触发器也保留任务
                    .build();
            scheduler.addJob(jobDetail, true);
        } else {
            jobDetail.getJobDataMap().put("param", param);
            scheduler.addJob(jobDetail, true, true); // 覆盖更新
        }
        Trigger oldTrigger = scheduler.getTrigger(triggerKey);
        if (oldTrigger != null) {
            scheduler.unscheduleJob(triggerKey);
        }
        CronTriggerImpl newTrigger = new CronTriggerImpl();
        newTrigger.setKey(triggerKey);
        newTrigger.setCronExpression(cron);
        newTrigger.setJobKey(jobKey);
        scheduler.scheduleJob(newTrigger);
        log.info("Quartz任务,taskId:{}新增/更新成功,新的Cron:{}",taskId,cron);
    }

5.1.3 停止任务

停止任务相当于把任务移除,包括触发器移除,所以恢复任务可以调用任务添加

    /**
     * 停止任务
     * @param taskId    任务key
     * @param jobGroup  任务组
     * @throws SchedulerException
     */
    public void stopTask(String taskId,String jobGroup) throws SchedulerException {
        TriggerKey triggerKey = new TriggerKey(TRIGGER_KEYNAME_PREFIX + taskId, jobGroup);
        scheduler.unscheduleJob(triggerKey);
        JobKey jobKey = new JobKey(taskId, jobGroup);
        scheduler.deleteJob(jobKey);
        log.info("Quartz任务,taskId:{}已停止",taskId);
    }

5.1.4 执行一次

普通的执行一次方法,参数使用的是初始化任务的默认参数

    public void triggerJobOnce(String taskId,String jobGroup) throws SchedulerException {
        // 根据任务ID构建JobKey(和创建任务时的JobKey保持一致!)
        JobKey jobKey = new JobKey(taskId, jobGroup);
        // 校验任务是否存在
        if (!scheduler.checkExists(jobKey)) {
            throw new IllegalArgumentException("任务ID【"+taskId+"】不存在,无法手动触发!");
        }
        // 核心API:手动触发,立即执行一次
        scheduler.triggerJob(jobKey);
        System.out.println("任务["+taskId+"]手动触发成功,已立即执行一次!");
    }

传入新的参数执行一次方法

    public void triggerJobOnce(String jobId,String jobGroup, Map<String, Object> paramMap) throws SchedulerException {
        JobKey jobKey = new JobKey(jobId, jobGroup);
        if (!scheduler.checkExists(jobKey)) {
            throw new IllegalArgumentException("任务ID【"+jobId+"】不存在,无法手动触发!");
        }
        // 构建临时参数Map,本次执行生效,不影响原任务
        JobDataMap jobDataMap = new JobDataMap(paramMap);
        // 核心API:手动触发+传参
        scheduler.triggerJob(jobKey, jobDataMap);
        System.out.println("任务["+jobId+"]手动触发成功,带参执行一次!");
    }

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

job每次执行都创建新的实例

 

posted @ 2020-05-12 10:17  苦心明  阅读(3541)  评论(0)    收藏  举报