任务调度的方式:Timer、ScheduledExecutorService、spring task、quartz、XXL-JOB、Elastic-Job

    1. 任务调度

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

      1. Timer

介绍

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

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

任务调度的方式:Timer、ScheduledExecutorService、spring task、quartz、XXL-JOB、Elastic-Job

参数说明:

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() + " : Annotation:is show run");

}

/**

秒执行一次

*/

@Scheduled(fixedRate = 1000 * 2)

public void print() {

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

}

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

@Scheduled(fixedDelay = 1 * 1000)

public void init() {

System.out.println(new Date() + ": Annotation:is 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,分布式集群开源工具,可以说是中小型公司必选,当然也视自身需求而定。

介绍

任务调度的方式:Timer、ScheduledExecutorService、spring task、quartz、XXL-JOB、Elastic-Job

两种方式实现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

, - * /

日期

月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简单和复杂例子

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" />

秒开始执行调度 -->

秒调度一次 -->

</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配置方式

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 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 {

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

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

// 触发器

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

// 触发器名,触发器组

triggerBuilder.withIdentity(triggerName, triggerGroupName);

triggerBuilder.startNow();

// 触发器时间设定

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

// 创建Trigger对象

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

// 调度容器设置JobDetail和Trigger

scheduler.scheduleJob(jobDetail, trigger);

// 启动

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));

//没有对应的job和trigger

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));

//没有对应的job和trigger

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));

//没有对应的job和trigger

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: 移除一个任务

*

* @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>

例子 3 Scheduler start/stop 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,并绑定到我们自定义的HelloJob的class对象

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

*/

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

秒就执行一次

// 触发器

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.quartz3;

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();

}

}

}

      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

上一篇:android webview内存泄露解决方法


下一篇:MUI框架开发HTML5手机APP(二)--页面跳转传值&底部选项卡切换