任务调度

    1. 任务调度

定时任务调度:基于给定的时间点、给定的时间间隔、给定的执行次数自动执行的任务。

 

 

      1. unix crontab命令

crontab [-u user] file

crontab [-u user] [ -e | -l | -r]

 

 

 

      1. Timer

介绍

Timer,简单无门槛,一般也没人用。单线程。

 

Timer位于java.util包下,其内部包含且仅包含一个后台线程(TimeThread)对多个业务任务(TimeTask)进行定时定频率的调度。

 

https://pic4.zhimg.com/80/v2-4fb47836439037bec04cadbb07c548f3_hd.jpg

参数说明:

task:所要执行的任务,需要extends TimeTask override run()

time/firstTime:首次执行任务的时间

period:周期性执行Task的时间间隔,单位是毫秒

delay:执行task任务前的延时时间,单位是毫秒

很显然,通过上述的描述,我们可以实现:

延迟多久后执行一次任务;指定时间执行一次任务;延迟一段时间,并周期性执行任务;指定时间,并周期性执行任务;

 

注意点

1:如果time/firstTime指定的时间,在当前时间之前,会发生什么呢?

在时间等于或者超过time/firstTime的时候,会执行task!也就是说,如果time/firstTime指定的时间在当前时间之前,就会立即得到执行。

延时问题!

 

2:schedule和scheduleAtFixedRate有什么区别?

scheduleAtFixedRate:每次执行时间为上一次任务开始起向后推一个period间隔,也就是说下次执行时间相对于上一次任务开始的时间点,因此执行时间不会延后,但是存在任务并发执行的问题。
schedule:每次执行时间为上一次任务结束后推一个period间隔,也就是说下次执行时间相对于上一次任务结束的时间点,因此执行时间会不断延后。

 

3:如果执行task发生异常,是否会影响其他task的定时调度?

如果TimeTask抛出RuntimeException,那么Timer会停止所有任务的运行!

 

4:Timer的一些缺陷?

前面已经提及到Timer背后是一个单线程,因此Timer存在管理并发任务的缺陷:所有任务都是由同一个线程来调度,所有任务都是串行执行,意味着同一时间只能有一个任务得到执行,而前一个任务的延迟或者异常会影响到之后的任务。
其次,Timer的一些调度方式还算比较简单,无法适应实际项目中任务定时调度的复杂度。

 

5:停止和移除

cancel():终止Timer计时器,丢弃所有当前已安排的任务(TimeTask也存在cancel()方法,不过终止的是TimeTask)

purge():从计时器的任务队列中移除已取消的任务,并返回个数

 

 

代码示例

 

package schedule.timer;

 

import java.text.SimpleDateFormat;

import java.util.Calendar;

import java.util.Timer;

import java.util.TimerTask;

 

/**

 * Timer背后是一个单线程,因此Timer存在管理并发任务的缺陷:所有任务都是由同一个线程来调度,所有任务都是串行执行,意味着同一时间只能有一个任务得到执行,而前一个任务的延迟或者异常会影响到之后的任务。

 * 其次,Timer的一些调度方式还算比较简单,无法适应实际项目中任务定时调度的复杂度。

 *

 *

 */

public class TimerTest {

    public static void main(String[] args) {

        Timer timer = new Timer();

       

        timer.schedule(new MyTask(), Calendar.getInstance().getTime(), 1000);

    }

}

 

class MyTask extends TimerTask{

    @Override

    public void run() {

        System.out.println("start at :"+new SimpleDateFormat("yyyy-MM-dd hh:mm:ss").format(this.scheduledExecutionTime()));

       

        try {

            Thread.sleep(2000);

        } catch (InterruptedException e) {

            e.printStackTrace();

        }

    }

}

 

      1. ScheduledExecutorService

介绍

JDK5之后便提供了基于线程池的定时任务调度:ScheduledExecutorService。

设计理念:每一个被调度的任务都会被线程池中的一个线程去执行,因此任务可以并发执行,而且相互之间不受影响。

 

示例

 

package schedule.scheduledExecutor;

 

import java.util.Date;

import java.util.concurrent.Executors;

import java.util.concurrent.ScheduledExecutorService;

import java.util.concurrent.TimeUnit;

 

public class ScheduledExecutorServiceTest {

    public static void main(String[] args) {

        ScheduledExecutorService scheduledExecutorService = Executors.newScheduledThreadPool(10);

        scheduledExecutorService.scheduleAtFixedRate(new ExecutorDemo(), 1000, 2000, TimeUnit.MILLISECONDS);

    }

}

 

class ExecutorDemo implements Runnable{

    @Override

    public void run() {

        System.out.println("执行"+new Date());

    }

}

 

 

      1. spring task

spring @Scheduled注解,一般集成于项目中,小任务很方便。

 

fixedRate和fixedDelay的区别

fixedRate :每隔多少毫秒执行一次该方法, 以上一次执行开始时间计算

fixedDelay:当一次方法执行完毕之后,延迟多少毫秒再执行该方法

 

 

例子1 注解方式

package schedule.springTask1;

 

import org.springframework.beans.BeansException;

import org.springframework.context.ApplicationContext;

import org.springframework.context.support.ClassPathXmlApplicationContext;

 

public class SpringTaskAnnotationTest {

    public static void main(String[] args) throws BeansException {

        ApplicationContext ctx = new ClassPathXmlApplicationContext("/schedule/springTask1/spring-mvc.xml");

    }

}

 

 

package schedule.springTask1;

 

import java.util.Date;

 

import org.springframework.scheduling.annotation.Scheduled;

import org.springframework.stereotype.Component;

 

/**

 * 基于注解的定时器

 */

@Component

public class SpringTaskAnnotation {

 

    // 定时计算。每一秒执行一次

    @Scheduled(cron = "0/1 * * * * *")

    public void show() {

        System.out.println(new Date() + " : Annotationis show run");

    }

 

    /**

     * 心跳更新。启动时执行一次,之后每隔2秒执行一次

     */

    @Scheduled(fixedRate = 1000 * 2)

    public void print() {

        System.out.println(new Date() + " : Annotationis print run");

    }

 

    // 启动加载缓存, 以上一次执行完为准

    @Scheduled(fixedDelay = 1 * 1000)

    public void init() {

        System.out.println(new Date() + ": Annotationis init run");

    }

}

 

<beans xmlns="http://www.springframework.org/schema/beans"

    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

    xmlns:p="http://www.springframework.org/schema/p"

    xmlns:task="http://www.springframework.org/schema/task"

    xmlns:context="http://www.springframework.org/schema/context"

    xmlns:aop="http://www.springframework.org/schema/aop"

    xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd 

        http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd   

        http://www.springframework.org/schema/task http://www.springframework.org/schema/task/spring-task.xsd">

 

    <!-- 定时器注解开关, 可以配置 scheduler参数 -->

    <task:annotation-driven />

    <!-- bean注解开关 -->

    <context:annotation-config />

    <!-- 自动扫描的包名 -->

    <context:component-scan base-package="schedule.springTask1" />

 

</beans>   

 

 

例子2 xml方式

package schedule.springTask2;

 

import org.springframework.beans.BeansException;

import org.springframework.context.ApplicationContext;

import org.springframework.context.support.ClassPathXmlApplicationContext;

 

public class SpringTaskXmlTest {

 

    public static void main(String[] args) throws BeansException {

        ApplicationContext ctx = new ClassPathXmlApplicationContext("/schedule/springTask2/spring-mvc.xml");

    }

}

 

 

package schedule.springTask2;

 

import java.util.Date;

 

/**

 * 基于xml的定时器

 */

public class SpringTaskXml {

 

    public void show() {

        System.out.println(new Date() + " : XMl is show run");

    }

 

    public void print() {

        System.out.println(new Date() + " : XMl print run");

    }

}

 

 

<beans xmlns="http://www.springframework.org/schema/beans"

    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

    xmlns:p="http://www.springframework.org/schema/p"

    xmlns:task="http://www.springframework.org/schema/task"

    xmlns:context="http://www.springframework.org/schema/context"

    xmlns:aop="http://www.springframework.org/schema/aop"

    xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd 

        http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd   

        http://www.springframework.org/schema/task http://www.springframework.org/schema/task/spring-task.xsd">

 

    <bean id="task" class="schedule.springTask2.SpringTaskXml"></bean>

 

    <task:scheduled-tasks>

        <!-- 这里表示的是每隔五秒执行一次 -->

        <task:scheduled ref="task" method="show"

            cron="*/5 * * * * ?" />

        <task:scheduled ref="task" method="print"

            cron="*/10 * * * * ?" />

    </task:scheduled-tasks>

 

</beans>

 

 

 

 

 

 

 

      1. Spring Quartz

开源工具 Quartz,分布式集群开源工具,可以说是中小型公司必选,当然也视自身需求而定。

 

介绍

https://pic3.zhimg.com/80/v2-4b73fe0a6efe3484883b16a9bb347ab9_hd.jpg

 

两种方式实现Spirng定时任务:

1.继承自org.springframework.scheduling.quartz.QuartzJobBean
2.作业类即普通的java类,不需要继承自任何基类。

 

定时器任务有两种触发器:

1.按照一定频度调用任务,在Spring Quartz中对应的触发器为:org.springframework.scheduling.quartz.SimpleTriggerBean
2.按照指定时间调用任务,在Spring Quartz中对应的调度器为:org.springframework.scheduling.quartz.CronTriggerBean

 

 

说明:
1、从代码上来看,有XxxBuilder、XxxFactory,说明Quartz用到了Builder、Factory模式,还有非常易懂的链式编程风格。
2、Quartz有3个核心概念:调度器(Scheduler)、任务(Job&JobDetail)、触发器(Trigger)。(一个任务可以被多个触发器触发,一个触发器只能触发一个任务)
3、注意当Scheduler调度Job时,实际上会通过反射newInstance一个新的Job实例(待调度完毕后销毁掉),同时会把JobExecutionContext传递给Job的execute方法,Job实例通过JobExecutionContext访问到Quartz运行时的环境以及Job本身的明细数据。
4、JobDataMap可以装载任何可以序列化的数据,存取很方便。需要注意的是JobDetail和Trigger都可以各自关联上JobDataMap。JobDataMap除了可以通过上述代码获取外,还可以在YourJob实现类中,添加相应setter方法获取。
5、Trigger用来告诉Quartz调度程序什么时候执行,常用的触发器有2种:SimpleTrigger(类似于Timer)、CronTrigger(类似于Linux的Crontab)。
6、实际上,Quartz在进行调度器初始化的时候,会加载quartz.properties文件进行一些属性的设置,比如Quartz后台线程池的属性(threadCount)、作业存储设置等。它会先从工程中找,如果找不到那么就是用quartz.jar中的默认的quartz.properties文件。
7、Quartz存在监听器的概念,比如任务执行前后、任务的添加等,可以方便实现任务的监控。

 

 

cronExpression配置说明

字段

允许值

允许的特殊字符

0-59

, - * /

0-59

, - * /

小时

0-23

, - * /

日期

1月31日

, - * ? / L W C

月份

1-12 或者 JAN-DEC

, - * /

星期

1-7 或者 SUN-SAT

, - * ? / L C #

年(可选)

留空, 1970-2099

, - * /

 

 

特殊字符说明:

字符

意义

*

表示所有值;

?

表示未说明的值,即不关心它为何值;

-

表示一个指定的范围;

,

表示附加一个可能值;

/

符号前表示开始时间,符号后表示每次递增的值;

L(“last”)

(“last”) “L” 用在day-of-month字段意思是

W(“weekday”)

只能用在day-of-month字段。用来描叙最接近指定天的工作日(周一到周五)。例如:在day-of-month字段用“15W”指“最接近这个月第15天的工作日”,即如果这个月第15天是周六,那么触发器将会在这个月第14天即周五触发;如果这个月第15天是周日,那么触发器将会在这个月第16 天即周一触发;如果这个月第15天是周二,那么就在触发器这天触发。注意一点:这个用法只会在当前月计算值,不会越过当前月。“W”字符仅能在day- of-month指明一天,不能是一个范围或列表。也可以用“LW”来指定这个月的最后一个工作日。

#

只能用在day-of-week字段。用来指定这个月的第几个周几。例:在day-of-week字段用”6#3”指这个月第3个周五(6指周五,3指第3个)。如果指定的日期不存在,触发器就不会触发。

C

指和calendar联系后计算过的值。例:在day-of-month 字段用“5C”指在这个月第5天或之后包括calendar的第一天;在day-of-week字段用“1C”指在这周日或之后包括calendar的第一天。

 

 

Cron表达式教程

这里给出一些常用的示例:
0 15 10 ? * 每天10点15分触发
0 15 10 ? 2017 2017年每天10点15分触发
0 14 * ? 每天下午的 2点到2点59分每分触发
0 0/5 14 ? 每天下午的 2点到2点59分(整点开始,每隔5分触发)
0 0/5 14,18 ? 每天下午的 2点到2点59分、18点到18点59分(整点开始,每隔5分触发)
0 0-5 14 ? 每天下午的 2点到2点05分每分触发
0 15 10 ? * 6L 每月最后一周的星期五的10点15分触发
0 15 10 ? * 6#3 每月的第三周的星期五开始触发
我们可以通过一些Cron在线工具非常方便的生成,比如http://www.pppet.net/等。

 

 

CronTrigger

CronTriggers往往比SimpleTrigger更有用,如果您需要基于日历的概念,而非SimpleTrigger完全指定的时间间隔,复发的发射工作的时间表。 CronTrigger,你可以指定触发的时间表如“每星期五中午”,或“每个工作日9:30时”,甚至“每5分钟一班9:00和10:00逢星期一上午,星期三星期五“。 即便如此,SimpleTrigger一样,CronTrigger拥有的startTime指定的时间表时生效,指定的时间表时,应停止(可选)结束时间。

Cron表达式

cron的表达式被用来配置CronTrigger实例。 cron的表达式是字符串,实际上是由七子表达式,描述个别细节的时间表。这些子表达式是分开的空白,代表:

1. Seconds

2. Minutes

3. Hours

4. Day-of-Month

5. Month

6. Day-of-Week

7. Year (可选字段)

例 "0 0 12 ? * WED" 在每星期三下午12:00 执行,

个别子表达式可以包含范围, 例如,在前面的例子里("WED")可以替换成 "MON-FRI", "MON, WED, FRI"甚至"MON-WED,SAT". “*” 代表整个时间段.

每一个字段都有一套可以指定有效值,如

Seconds (秒) :可以用数字0-59 表示,

Minutes(分) :可以用数字0-59 表示,

Hours(时) :可以用数字0-23表示,

Day-of-Month(天) :可以用数字1-31 中的任一一个值,但要注意一些特别的月份

Month(月) :可以用0-11 或用字符串 “JAN, FEB, MAR, APR, MAY, JUN, JUL, AUG, SEP, OCT, NOV and DEC” 表示

Day-of-Week(每周):可以用数字1-7表示(1 = 星期日)或用字符口串“SUN, MON, TUE, WED, THU, FRI and SAT”表示

“/”:为特别单位,表示为“每”如“0/15”表示每隔15分钟执行一次,“0”表示为从“0”分开始, “3/20”表示表示每隔20分钟执行一次,“3”表示从第3分钟开始执行

“?”:表示每月的某一天,或第周的某一天

“L”:用于每月,或每周,表示为每月的最后一天,或每个月的最后星期几如“6L”表示“每月的最后一个星期五”

“W”:表示为最近工作日,如“15W”放在每月(day-of-month)字段上表示为“到本月15日最近的工作日”

““#”:是用来指定“的”每月第n个工作日,例 在每周(day-of-week)这个字段中内容为"6#3" or "FRI#3" 则表示“每月第三个星期五”

 

 

例子 1 cron简单和复杂例子

可指定执行类和指定方法,可以注入service

 

package schedule.quartz1;

 

import org.springframework.beans.BeansException;

import org.springframework.context.ApplicationContext;

import org.springframework.context.support.ClassPathXmlApplicationContext;

 

public class SpringTaskAnnotationTest {

    public static void main(String[] args) throws BeansException {

        ApplicationContext ctx = new ClassPathXmlApplicationContext("/schedule/quartz1/spring-mvc.xml");

    }

}

 

 

package schedule.quartz1;

 

public class SimpleJob {

    protected void execute() {

        System.out.println("简单定时任务执行中…");

    }

}

 

 

package schedule.quartz1;

 

import org.quartz.JobExecutionContext;

import org.quartz.JobExecutionException;

import org.springframework.scheduling.quartz.QuartzJobBean;

 

public class ComplexJob extends QuartzJobBean {

 

    private int timeout;

 

    // 调度工厂实例化后,经过timeout时间开始执行调度

    public void setTimeout(int timeout) {

        this.timeout = timeout;

    }

 

    /**

     * 要调度的具体任务

     */

    @Override

    protected void executeInternal(JobExecutionContext context) throws JobExecutionException {

        System.out.println("复杂定时任务执行中…");

    }

}

 

 

 

<beans xmlns="http://www.springframework.org/schema/beans"

    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

    xmlns:p="http://www.springframework.org/schema/p"

    xmlns:task="http://www.springframework.org/schema/task"

    xmlns:context="http://www.springframework.org/schema/context"

    xmlns:aop="http://www.springframework.org/schema/aop"

    xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd 

        http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd   

        http://www.springframework.org/schema/task http://www.springframework.org/schema/task/spring-task.xsd">

       

    <bean id="simpleJob" class="schedule.quartz1.SimpleJob"></bean>

   

    <!-- 使用 MethodInvokingJobDetailFactoryBean, 指定特定方法 -->

    <bean id="simpleJobDetail" class="org.springframework.scheduling.quartz.MethodInvokingJobDetailFactoryBean">

        <property name="targetObject" ref="simpleJob" />

        <property name="targetMethod" value="execute" />

        <property name="concurrent" value="false" /><!-- 作业不并发调度 -->

    </bean>

   

    <!-- 简单触发器,使用 SimpleTriggerFactoryBean -->

    <bean id="simpleTrigger"  class="org.springframework.scheduling.quartz.SimpleTriggerFactoryBean">

        <property name="jobDetail" ref="simpleJobDetail" />

        <property name="startDelay" value="1000" /><!-- 调度工厂实例化后,经过1000秒开始执行调度 -->

        <property name="repeatInterval" value="2000" /><!-- 2秒调度一次 -->

    </bean>

 

 

    <!-- 高级设置, 给作业传递数据 -->

    <bean id="complexJobDetail" class="org.springframework.scheduling.quartz.JobDetailFactoryBean">

        <property name="jobClass" value="schedule.quartz1.ComplexJob" />

 

        <property name="jobDataMap">

            <map>

                <entry key="timeout" value="1" />

            </map>

        </property>

 

        <property name="durability" value="true" />

    </bean>

 

    <!-- 计划触发器,使用 CronTriggerFactoryBean -->

    <bean id="cronTrigger" class="org.springframework.scheduling.quartz.CronTriggerFactoryBean">

        <property name="jobDetail" ref="complexJobDetail" />

        <!--<property name="cronExpression" value="0/5 * * ? * SAT-SUN" /> -->

        <property name="cronExpression" value="0/5 * * ? * *" />

    </bean>

 

    <!-- 调度器 -->

    <bean

        class="org.springframework.scheduling.quartz.SchedulerFactoryBean">

        <property name="triggers">

            <list>

                <ref bean="simpleTrigger" />

                <ref bean="cronTrigger" />

            </list>

        </property>

    </bean>

 

</beans>

 

 

例子 2 Scheduler start/stop manager配置方式

可以指定执行类(继承job),但不能注入service

 

package schedule.quartz2;

 

import org.quartz.SchedulerException;

import org.springframework.beans.BeansException;

import org.springframework.context.ApplicationContext;

import org.springframework.context.support.ClassPathXmlApplicationContext;

 

public class SpringTaskAnnotationTest {

    public static void main(String[] args) throws BeansException, InterruptedException, SchedulerException {

        ApplicationContext applicationContext = new ClassPathXmlApplicationContext("/schedule/quartz2/spring-mvc.xml");

       

        QuartzManager quartzManager = applicationContext.getBean(QuartzManager.class);

        quartzManager.initJobs();

       

       

        quartzManager.addJob("xxxx", "yyyy", "zzzz", "gggg",MyJob.class, "0/3 * * ? * *");

       

       

        Thread.sleep(2000);

        System.out.println("--change to 2s--");

       

        quartzManager.trigger("xxxxcc", "yyyy", "zzzzx", "ggggx",MyJob2.class, "0/1 * * ? * *");

       

       

        quartzManager.modifyJobTime("xxxx", "yyyy", "zzzz", "gggg", "0/2 * * ? * *");

       

//      Thread.sleep(20000);

       

        System.out.println("--stop--");

       

//      quartzManager.shutdownJobs();

        Thread.sleep(20000);

    }

}

 

 

package schedule.quartz2;

 

import java.util.HashSet;

import java.util.List;

 

import org.apache.commons.lang3.StringUtils;

import org.quartz.CronScheduleBuilder;

import org.quartz.CronTrigger;

import org.quartz.JobBuilder;

import org.quartz.JobDetail;

import org.quartz.JobKey;

import org.quartz.Scheduler;

import org.quartz.SchedulerException;

import org.quartz.Trigger;

import org.quartz.TriggerBuilder;

import org.quartz.TriggerKey;

import org.springframework.beans.factory.annotation.Autowired;

 

public class QuartzManager {

 

    private Scheduler scheduler;

   

    @Autowired

    private IJobService jobService;

   

    //初始化所有的jobs

    public void initJobs() {

        Long count = jobService.listQuartzEntity(null);

       

        if(0!=count) {

            System.out.println(".....");

        }

    }

 

    /**

     * @Description: 添加一个定时任务

     *

     * @param jobName          任务名

     * @param jobGroupName     任务组名

     * @param triggerName      触发器名

     * @param triggerGroupName 触发器组名

     * @param jobClass         任务

     * @param cron             时间设置,参考quartz说明文档

     */

    @SuppressWarnings({ "unchecked", "rawtypes" })

    public void addJob(String jobName, String jobGroupName, String triggerName, String triggerGroupName, Class jobClass, String cron) {

        try {

            // 任务名,任务组,任务执行类

            JobKey jobKey = JobKey.jobKey(jobName, jobGroupName);

            JobDetail jobDetail = null;

            if(!scheduler.checkExists(jobKey)){

                jobDetail = JobBuilder.newJob(jobClass).withIdentity(jobName, jobGroupName).build();

            }else{

                jobDetail = scheduler.getJobDetail(jobKey);

            }

           

            TriggerKey triggerKey = new TriggerKey(triggerName, triggerGroupName);

            if(!scheduler.checkExists(triggerKey)){

                // 触发器

                TriggerBuilder<Trigger> triggerBuilder = TriggerBuilder.newTrigger();

                // 触发器名,触发器组

                triggerBuilder.withIdentity(triggerName, triggerGroupName);

                triggerBuilder.startNow();

                // 触发器时间设定

                triggerBuilder.withSchedule(CronScheduleBuilder.cronSchedule(cron));

                // 创建Trigger对象

                CronTrigger trigger = (CronTrigger) triggerBuilder.build();

               

                // 调度容器设置JobDetailTrigger

                scheduler.scheduleJob(jobDetail, trigger);

//              scheduler.rescheduleJob(triggerKey, trigger);

               

                //https://stackoverflow.com/questions/34176482/unable-to-store-job-because-one-already-exists-with-this-identification

               

                // 启动

                if (!scheduler.isShutdown()) {

                    scheduler.start();

                }

            }

        } catch (Exception e) {

            throw new RuntimeException(e);

        }

    }

   

    public void addJobs(String jobName, String jobGroupName, List<SingleTrigger> triggers, Class jobClass, String cron) {

        try {

            // 任务名,任务组,任务执行类

            JobDetail jobDetail = scheduler.getJobDetail(JobKey.jobKey(jobName, jobGroupName));

            if(null==jobDetail){

                jobDetail = JobBuilder.newJob(jobClass).withIdentity(jobName, jobGroupName).build();

            }

           

            if(null!=triggers && !triggers.isEmpty()){

                HashSet<Trigger> triggerSet = new HashSet<Trigger>();

               

                for (SingleTrigger singleTrigger : triggers) {

                    if(null==scheduler.getTrigger(new TriggerKey(singleTrigger.getTriggerName(), singleTrigger.getTriggerGroup()))){

                        // 触发器

                        TriggerBuilder<Trigger> triggerBuilder = TriggerBuilder.newTrigger();

                        // 触发器名,触发器组

                        triggerBuilder.withIdentity(singleTrigger.getTriggerName(), singleTrigger.getTriggerGroup());

                        triggerBuilder.startNow();

                        // 触发器时间设定

                        triggerBuilder.withSchedule(CronScheduleBuilder.cronSchedule(singleTrigger.getTriggerCron()));

                        // 创建Trigger对象

                        CronTrigger trigger = (CronTrigger) triggerBuilder.build();

                       

                        triggerSet.add(trigger);

                    }

                }

               

                // 调度容器设置JobDetailTrigger

                scheduler.scheduleJob(jobDetail, triggerSet, true);

               

                // 启动

                if (!scheduler.isShutdown()) {

                    scheduler.start();

                }

            }

        } catch (Exception e) {

            throw new RuntimeException(e);

        }

    }

   

   

   

    /**

     * 触发任务

     * @throws SchedulerException

     */

    public void trigger(String jobName, String jobGroupName, String triggerName, String triggerGroupName, Class jobClass, String cron) throws SchedulerException {

        JobDetail jobDetail = scheduler.getJobDetail(JobKey.jobKey(jobName, jobGroupName));

       

        //没有对应的jobtrigger

        if(null==jobDetail && null==scheduler.getTrigger(new TriggerKey(triggerName, triggerGroupName))) {

            addJob(jobName, jobGroupName, triggerName, triggerGroupName, jobClass, cron);

        }else {

            JobKey key = new JobKey(jobName, jobGroupName);

            scheduler.triggerJob(key);

        }

    }

   

    /**

     * 停止任务

     * @throws SchedulerException

     */

    public void pauseJob(String jobName, String jobGroupName, String triggerName, String triggerGroupName, Class jobClass, String cron) throws SchedulerException {

        JobDetail jobDetail = scheduler.getJobDetail(JobKey.jobKey(jobName, jobGroupName));

       

        //没有对应的jobtrigger

        if(null!=jobDetail && null!=scheduler.getTrigger(new TriggerKey(triggerName, triggerGroupName))) {

            JobKey key = new JobKey(jobName, jobGroupName);

            scheduler.pauseJob(key);

        }

    }

   

    /**

     * 恢复任务

     * @throws SchedulerException

     */

    public void resumeJob(String jobName, String jobGroupName, String triggerName, String triggerGroupName, Class jobClass, String cron) throws SchedulerException {

        JobDetail jobDetail = scheduler.getJobDetail(JobKey.jobKey(jobName, jobGroupName));

       

        //没有对应的jobtrigger

        if(null!=jobDetail && null!=scheduler.getTrigger(new TriggerKey(triggerName, triggerGroupName))) {

            JobKey key = new JobKey(jobName, jobGroupName);

            scheduler.resumeJob(key);

        }

    }  

 

    /**

     * @Description: 修改一个任务的触发时间

     *

     * @param jobName

     * @param jobGroupName

     * @param triggerName      触发器名

     * @param triggerGroupName 触发器组名

     * @param cron             时间设置,参考quartz说明文档

     */

    public void modifyJobTime(String jobName, String jobGroupName, String triggerName, String triggerGroupName, String cron) {

        try {

            TriggerKey triggerKey = TriggerKey.triggerKey(triggerName, triggerGroupName);

            CronTrigger trigger = (CronTrigger) scheduler.getTrigger(triggerKey);

            if (trigger == null) {

                return;

            }

 

            String oldTime = trigger.getCronExpression();

            if (!oldTime.equalsIgnoreCase(cron)) {

                /** 方式一 :调用 rescheduleJob 开始 */

                // 触发器

                TriggerBuilder<Trigger> triggerBuilder = TriggerBuilder.newTrigger();

                // 触发器名,触发器组

                triggerBuilder.withIdentity(triggerName, triggerGroupName);

                triggerBuilder.startNow();

                // 触发器时间设定

                triggerBuilder.withSchedule(CronScheduleBuilder.cronSchedule(cron));

                // 创建Trigger对象

                trigger = (CronTrigger) triggerBuilder.build();

                // 方式一 :修改一个任务的触发时间

                scheduler.rescheduleJob(triggerKey, trigger);

                /** 方式一 :调用 rescheduleJob 结束 */

 

                /** 方式二:先删除,然后在创建一个新的Job */

                // JobDetail jobDetail =

                // scheduler.getJobDetail(JobKey.jobKey(jobName, jobGroupName));

                // Class<? extends Job> jobClass = jobDetail.getJobClass();

                // removeJob(jobName, jobGroupName, triggerName,

                // triggerGroupName);

                // addJob(jobName, jobGroupName, triggerName, triggerGroupName,

                // jobClass, cron);

                /** 方式二 :先删除,然后在创建一个新的Job */

            }

        } catch (Exception e) {

            throw new RuntimeException(e);

        }

    }

   

   

    /**

     * @Description: 移除一个trigger

     *

     * @param jobName

     * @param jobGroupName

     * @param triggerName

     * @param triggerGroupName

     */

    public void removeTrigger(String jobName, String jobGroupName, String triggerName, String triggerGroupName) {

        try {

            TriggerKey triggerKey = TriggerKey.triggerKey(triggerName, triggerGroupName);

 

            scheduler.pauseTrigger(triggerKey);// 停止触发器

            scheduler.unscheduleJob(triggerKey);// 移除触发器

        } catch (Exception e) {

            throw new RuntimeException(e);

        }

    }

   

 

    /**

     * @Description: 移除一个任务

     *

     * @param jobName

     * @param jobGroupName

     * @param triggerName

     * @param triggerGroupName

     */

    public void removeJob(String jobName, String jobGroupName, String triggerName, String triggerGroupName) {

        try {

            TriggerKey triggerKey = TriggerKey.triggerKey(triggerName, triggerGroupName);

 

            scheduler.pauseTrigger(triggerKey);// 停止触发器

            scheduler.unscheduleJob(triggerKey);// 移除触发器

            scheduler.deleteJob(JobKey.jobKey(jobName, jobGroupName));// 删除任务

        } catch (Exception e) {

            throw new RuntimeException(e);

        }

    }

 

    /**

     * @Description:启动所有定时任务

     */

    public void startJobs() {

        try {

            scheduler.start();

        } catch (Exception e) {

            throw new RuntimeException(e);

        }

    }

 

    /**

     * @Description:关闭所有定时任务

     */

    public void shutdownJobs() {

        try {

            if (!scheduler.isShutdown()) {

                scheduler.shutdown();

            }

        } catch (Exception e) {

            throw new RuntimeException(e);

        }

    }

 

    public Scheduler getScheduler() {

        return scheduler;

    }

 

    public void setScheduler(Scheduler scheduler) {

        this.scheduler = scheduler;

    }

 

    public String transCron(String time) {

        String seconds = StringUtils.substringAfterLast(time, ":");

        String minute = StringUtils.substringAfter(time, ":").substring(0, 2);

        String hour = StringUtils.substringAfter(time, " ").substring(0, 2);

        String day = StringUtils.substringAfterLast(time, "-").substring(0, 2);

        String month = StringUtils.substringAfter(time, "-").substring(0, 2);

        return seconds + " " + minute + " " + hour + " " + day + " " + month + " ?";

    }

}

 

 

package schedule.quartz2;

 

import lombok.Data;

 

@Data

public class QuartzEntity{

    private String jobName;//任务名称

    private String jobGroup;//任务分组

    private String description;//任务描述

    private String jobClassName;//执行类

    private String cronExpression;//执行时间

    private String triggerName;//执行时间

    private String triggerState;//任务状态

   

    private String oldJobName;//任务名称 用于修改

    private String oldJobGroup;//任务分组 用于修改

}

 

 

package schedule.quartz2;

 

import org.quartz.Job;

import org.quartz.JobExecutionContext;

import org.quartz.JobExecutionException;

import org.slf4j.Logger;

import org.slf4j.LoggerFactory;

 

public class MyJob implements Job {

 

    private Logger logger = LoggerFactory.getLogger(MyJob.class);

 

    @Override

    public void execute(JobExecutionContext context) throws JobExecutionException {

        try {

            logger.info("test】动态定时调度测试");

        } catch (Exception e) {

            e.printStackTrace();

        }

    }

}

 

package schedule.quartz2;

 

import org.quartz.Job;

import org.quartz.JobExecutionContext;

import org.quartz.JobExecutionException;

import org.slf4j.Logger;

import org.slf4j.LoggerFactory;

 

public class MyJob2 implements Job {

 

    private Logger logger = LoggerFactory.getLogger(MyJob2.class);

 

    @Override

    public void execute(JobExecutionContext context) throws JobExecutionException {

        try {

            logger.info("test】动态定时调度测试 ------------2");

        } catch (Exception e) {

            e.printStackTrace();

        }

    }

}

 

package schedule.quartz2;

 

import java.util.List;

 

public interface IJobService {

    List<QuartzEntity> listQuartzEntity(QuartzEntity quartz,Integer pageNo,Integer pageSize);

    Long listQuartzEntity(QuartzEntity quartz); 

}

 

 

package schedule.quartz2;

import java.util.List;

 

import org.springframework.beans.factory.annotation.Autowired;

import org.springframework.stereotype.Service;

import org.springframework.util.StringUtils;

 

@Service("jobService")

public class JobServiceImpl implements IJobService {

 

    @Autowired

    private DynamicQuery dynamicQuery;

 

    @Override

    public List<QuartzEntity> listQuartzEntity(QuartzEntity quartz,

            Integer pageNo, Integer pageSize) {

        StringBuffer nativeSql = new StringBuffer();

        nativeSql.append("SELECT job.JOB_NAME as jobName,job.JOB_GROUP as jobGroup,job.DESCRIPTION as description,job.JOB_CLASS_NAME as jobClassName,");

        nativeSql.append("cron.CRON_EXPRESSION as cronExpression,tri.TRIGGER_NAME as triggerName,tri.TRIGGER_STATE as triggerState,");

        nativeSql.append("job.JOB_NAME as oldJobName,job.JOB_GROUP as oldJobGroup ");

        nativeSql.append("FROM qrtz_job_details AS job LEFT JOIN qrtz_triggers AS tri ON job.JOB_NAME = tri.JOB_NAME ");

        nativeSql.append("LEFT JOIN qrtz_cron_triggers AS cron ON cron.TRIGGER_NAME = tri.TRIGGER_NAME ");

        nativeSql.append("WHERE tri.TRIGGER_TYPE = 'CRON'");

        Object[] params = new  Object[]{};

        if(!StringUtils.isEmpty(quartz.getJobName())){//加入JobName搜索其他条件自行实现

            nativeSql.append(" AND job.JOB_NAME = ?");

            params = new Object[]{quartz.getJobName()};

        }

        return null;

    }

 

    @Override

    public Long listQuartzEntity(QuartzEntity quartz) {

        return 0L;

       

//      StringBuffer nativeSql = new StringBuffer();

//      nativeSql.append("SELECT COUNT(*)");

//      nativeSql.append("FROM qrtz_job_details AS job LEFT JOIN qrtz_triggers AS tri ON job.JOB_NAME = tri.JOB_NAME ");

//      nativeSql.append("LEFT JOIN qrtz_cron_triggers AS cron ON cron.TRIGGER_NAME = tri.TRIGGER_NAME ");

//      nativeSql.append("WHERE tri.TRIGGER_TYPE = 'CRON'");

//      return dynamicQuery.nativeQueryCount(nativeSql.toString(), new Object[]{});

    }

}

 

 

package schedule.quartz2;

import java.util.List;

/**

 * 扩展SpringDataJpa, 支持动态jpql/nativesql查询并支持分页查询

 * 使用方法:注入ServiceImpl

 */

public interface DynamicQuery {

   

   

}

 

 

package schedule.quartz2;

import org.springframework.stereotype.Repository;

 

@Repository

public class DynamicQueryImpl implements DynamicQuery {

   

   

}

 

 

<beans xmlns="http://www.springframework.org/schema/beans"

    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

    xmlns:p="http://www.springframework.org/schema/p"

    xmlns:task="http://www.springframework.org/schema/task"

    xmlns:context="http://www.springframework.org/schema/context"

    xmlns:aop="http://www.springframework.org/schema/aop"

    xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd 

        http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd   

        http://www.springframework.org/schema/task http://www.springframework.org/schema/task/spring-task.xsd">

    <context:component-scan base-package="schedule.quartz2"></context:component-scan>

 

    <bean id="startQuartz" lazy-init="true" autowire="no"

        class="org.springframework.scheduling.quartz.SchedulerFactoryBean">

        <property name="triggers">

            <list>

                <!--

                <ref bean="cronTrigger" />

                 -->

            </list>

        </property>

    </bean>

 

    <bean id="quartzManager" class="schedule.quartz2.QuartzManager" lazy-init="false" init-method="startJobs">

        <!--这个对象一定要注入,这样类才能进行管理,还有在类型要用get set方法,不然会报错。 -->

        <property name="scheduler" ref="startQuartz" />

    </bean>

 

 

    <!-- 高级设置, 给作业传递数据 -->

    <bean id="complexJobDetail" class="org.springframework.scheduling.quartz.JobDetailFactoryBean">

        <property name="jobClass" value="schedule.quartz2.MyJob" />

 

        <property name="jobDataMap">

            <map>

                <entry key="timeout" value="1" />

            </map>

        </property>

 

        <property name="durability" value="true" />

    </bean>

 

    <!-- 计划触发器,使用 CronTriggerFactoryBean -->

    <bean id="cronTrigger" class="org.springframework.scheduling.quartz.CronTriggerFactoryBean">

        <property name="jobDetail" ref="complexJobDetail" />

        <!--<property name="cronExpression" value="0/5 * * ? * SAT-SUN" /> -->

        <property name="cronExpression" value="0/5 * * ? * *" />

    </bean>

</beans>

 

 

 

 

 

 

 

package schedule.quartz2;

 

import java.util.ArrayList;

import java.util.Arrays;

import java.util.List;

 

import org.quartz.SchedulerException;

import org.springframework.beans.BeansException;

import org.springframework.context.ApplicationContext;

import org.springframework.context.support.ClassPathXmlApplicationContext;

 

public class SpringTaskAnnotationTest {

    public static void main(String[] args) throws BeansException, InterruptedException, SchedulerException {

        ApplicationContext applicationContext = new ClassPathXmlApplicationContext("/schedule/quartz2/spring-mvc.xml");

       

        QuartzManager quartzManager = applicationContext.getBean(QuartzManager.class);

        quartzManager.initJobs();

       

        List<SingleTrigger> triggers = new ArrayList<SingleTrigger>();

        triggers.add(new SingleTrigger("a1","b1","0/1 * * ? * *"));

        triggers.add(new SingleTrigger("a2","b2","0/2 * * ? * *"));

        triggers.add(new SingleTrigger("a3","b3","0/3 * * ? * *"));

        triggers.add(new SingleTrigger("a4","b4","0/4 * * ? * *"));

       

        quartzManager.addJobs("xxxx", "yyyy", triggers ,MyJob.class, "0/3 * * ? * *");

       

        Thread.sleep(7000);

        System.out.println("--- start to change cron ---");

       

        quartzManager.removeTrigger("xxxx", "yyyy", "a1","b1");

        quartzManager.modifyJobTime("xxxx", "yyyy", "a2","b2", "0/5 * * ? * *");

       

        Thread.sleep(7000);

       

        System.out.println("--- start to replace cron ---");

        triggers = new ArrayList<SingleTrigger>();

        triggers.add(new SingleTrigger("a5","b5","0/1 * * ? * *"));

        quartzManager.addJobs("xxxx", "yyyy", triggers ,MyJob.class, "0/3 * * ? * *");

       

        System.out.println(quartzManager.getScheduler().getTriggerGroupNames());

       

       

        Thread.sleep(200000);

    }

}

 

 

 

 

 

例子 3 Scheduler start/stop java方式

java方式初始化

 

package schedule.quartz3;

 

import org.quartz.JobBuilder;

import org.quartz.JobDetail;

import org.quartz.Scheduler;

import org.quartz.SimpleScheduleBuilder;

import org.quartz.Trigger;

import org.quartz.TriggerBuilder;

import org.quartz.impl.StdSchedulerFactory;

 

public class QuartzTest {

    public static void main(String[] args) throws Exception {

        // 从调度程序工厂获取一个调度程序的实例

        Scheduler scheduler = StdSchedulerFactory.getDefaultScheduler();

 

        // 显示调度程序的名称(这里会展示我们在quartz.properties文件中的名称)

        System.out.println("scheduleName = " + scheduler.getSchedulerName());

 

        /**

         * 重要: 定义一个job,并绑定到我们自定义的HelloJobclass对象

         * 这里并不会马上创建一个HelloJob实例,实例创建是在scheduler安排任务触发执行时创建的 这种机制也为后面使用Spring集成提供了便利

         */

        JobDetail job = JobBuilder.newJob(MyJob.class).withIdentity("job1", "group1").build();

 

        // 声明一个触发器,现在就执行(schedule.start()方法开始调用的时候执行);并且每间隔2秒就执行一次

        // 触发器

        TriggerBuilder<Trigger> triggerBuilder = TriggerBuilder.newTrigger();

        // 触发器名,触发器组

        triggerBuilder.withIdentity("trigger1", "group1");

        triggerBuilder.startNow();

        // 触发器时间设定

//      triggerBuilder.withSchedule(CronScheduleBuilder.cronSchedule("0/3 * * ? * *"));

    triggerBuilder.withSchedule(SimpleScheduleBuilder.simpleSchedule().withIntervalInSeconds(3).repeatForever());

       

       

        Trigger trigger = triggerBuilder.build();

 

        // 告诉quartz使用定义的触发器trigger安排执行任务job

        scheduler.scheduleJob(job, trigger);

 

        // 启动任务调度程序,内部机制是线程的启动

        scheduler.start();

 

        // 关闭任务调度程序,如果不关闭,调度程序schedule会一直运行着

        // scheduler.shutdown();

 

    }

}

 

package schedule.quartz2;

 

import org.quartz.Job;

import org.quartz.JobExecutionContext;

import org.quartz.JobExecutionException;

import org.quartz.impl.triggers.CronTriggerImpl;

import org.quartz.impl.triggers.SimpleTriggerImpl;

import org.slf4j.Logger;

import org.slf4j.LoggerFactory;

 

public class MyJob implements Job {

    private Logger logger = LoggerFactory.getLogger(MyJob.class);

 

    @Override

    public void execute(JobExecutionContext context) throws JobExecutionException {

        try {

            if(context.getTrigger() instanceof CronTriggerImpl){

                CronTriggerImpl xxx = ((CronTriggerImpl)context.getTrigger());

               

                logger.info("动态定时调度测试 job group:"+xxx.getJobGroup()+", job name:"+xxx.getJobName() +", trigger group:"+xxx.getGroup()+", trigger name:"+xxx.getName()+",  cronExpression "+xxx.getCronExpression());

            }else if(context.getTrigger() instanceof SimpleTriggerImpl){

                SimpleTriggerImpl xxx = ((SimpleTriggerImpl)context.getTrigger());

               

                logger.info("动态定时调度测试 job group:"+xxx.getJobGroup()+", job name:"+xxx.getJobName() +", trigger group:"+xxx.getGroup()+", trigger name:"+xxx.getName()+",  cronExpression "+xxx);

            }

           

        } catch (Exception e) {

            e.printStackTrace();

        }

    }

}

 

例子4 注入service

SchedulerFactoryBean+AdaptableJobFactory+QuartzJobBean

 

 

 

Caused by: java.lang.NoSuchMethodError: org.springframework.util.ReflectionUtils.accessibleConstructor(Ljava/lang/Class;[Ljava/lang/Class;)Ljava/lang/reflect/Constructor;

 

一般是引入了两个重复的spring Jar包,如既引入了spring.framework 又引入了spring.core,去掉一个就好

用tree方式查看

 

 

 

 

 

 

 

 

      1. XXL-JOB

 

分布式任务 XXL-JOB,是一个轻量级分布式任务调度框架,支持通过 Web 页面对任务进行 CRUD 操作,支持动态修改任务状态、暂停/恢复任务,以及终止运行中任务,支持在线配置调度任务入参和在线查看调度结果。

 

https://github.com/xuxueli/xxl-job

 

https://www.cnblogs.com/xuxueli/p/5021979.html

 

 

 

 

 

 

 

 

 

 

 

      1. Elastic-Job

分布式任务 Elastic-Job,是一个分布式调度解决方案,由两个相互独立的子项目 Elastic-Job-Lite 和 Elastic-Job-Cloud 组成。定位为轻量级无中心化解决方案,使用 jar 包的形式提供分布式任务的协调服务。支持分布式调度协调、弹性扩容缩容、失效转移、错过执行作业重触发、并行调度、自诊。

 

 

https://gitee.com/52itstyle/spring-boot-quartz

 

 

原文地址:https://www.cnblogs.com/xiang--liu/p/9710090.html