Quartz定时任务

基本介绍

Quartz是OpenSymphony开源组织在Job scheduling领域又一个开源项目,可以与J2EE与J2SE应用程序相结合也可以单独使用。Quartz可以用来创建简单或为运行十个、百个、甚至是好几万个Jobs这样复杂的日程序表,Jobs可以做成标准的Java组件或EJBs
Quartz是一个任务日程管理系统,一个在预先确定(被纳入日程)的时间到达时,负责执行(或者通知)其他软件组件的系统
Quartz用一个小Java库发布文件(.jar文件),这个库文件包含了所有Quartz核心功能,这些功能的主要接口(API)是Scheduler接口,它提供了简单的操作
例如:将任务纳入日程或者从日程中取消,开始、停止、暂停日程进度

(1)完全由Java编写的开源作业调度框架,为在Java应用程序中进行作业调度提供了简单却强大的机制
(2)可以与J2EE与J2SE应用程序相结合也可以单独使用
(3)允许程序开发人员根据时间的间隔来调度作业
(4)实现了作业和触发器的多对多的关系,还能把多个作业与不同的触发器关联
(5)免费使用,并根据Apache 2.0许可

Quartz拥有完善的事件和监听体系,大部分组件都拥有事件,如任务执行前事件、任务执行后事件、触发器触发前事件、触发后事件、调度器开始事件、关闭事件等等,可以注册相应的监听器处理感兴趣的事件。


下载

1.进入官网

http://www.quartz-scheduler.org/
Quartz定时任务_第1张图片

2.选择版本下载

Quartz定时任务_第2张图片

3.文件目录说明

Quartz定时任务_第3张图片


执行流程

Quartz定时任务_第4张图片
Job为作业的接口,为任务调度的对象;JobDetail用来描述Job的实现类及其他相关的静态信息;Trigger做为作业的定时管理工具,一个Trigger只能对应一个作业实例,而一个作业实例可对应多个触发器;Scheduler做为定时任务容器,是Quartz最上层的东西,它提携了所有触发器和作业,使它们协调工作,每个Scheduler都存有JobDetail和Trigger的注册,一个Scheduler中可以注册多个JobDetail和多个Trigger


核心接口

(1)Job(任务接口,无状态)

开发者实现该接口定义运行任务。表示一个工作,要执行的具体内容。此接口中只有一个方法

public interface Job {
    void execute(JobExecutionContext context) throws JobExecutionException;
}

Job是一个执行任务的简单java类。任务可以是任何java代码
只需实现org.quartz.Job接口,并将需要执行的任务代码写在execute()中即可。当Quartz确定该是作业运行的时候,它将调用作业。Quartz提供了一个机制来建立具有不同粒度的、可重复的调度表,于是,只需创建一个java类,这个类被调用而执行任务

描述
JobExecutionContext类 提供了调度上下文的各种信息。Job运行时的信息保存在JobDataMap实例中

(2)StatefulJob(任务接口,有状态)

任务在执行任务过程中,任何对Job Data Map所作的更改都将丢失而且任务下次执行时也无法看到。
StatefulJob在任务的每次执行之后重新存储JobDataMap

Job vs. StatefulJob
JobDataMap在每次执行之后重新持久化到JobStore中。
两个或多个有状态的JobDetail实例不能并发执行

StatefulJob子接口
Job有一个StatefulJob子接口,代表有状态的任务,该接口是一个没有方法的标签接口,其目的是让Quartz知道任务的类型,以便采用不同的执行方案。无状态任务在执行时拥有自己的JobDataMap拷贝,对JobDataMap的更改不会影响下次的执行。而有状态任务共享共享同一个 JobDataMap实例,每次任务执行对JobDataMap所做的更改会保存下来,每次执行任务后都会对后面的执行发生影响。
正因为这个原因,无状态的Job可以并发执行,而有状态的StatefulJob不能并发执行,这意味着如果前次的StatefulJob还没有执行完毕,下一次的任务将阻塞等待,直到前次任务执行完毕。有状态任务比无状态任务需要考虑更多的因素,程序往往拥有更高的复杂度,因此除非必要,应该尽量使用无状态的Job
如果Quartz使用了数据库持久化任务调度信息,无状态的JobDataMap仅会在Scheduler注册任务时保持一次,而有状态任务对应的JobDataMap在每次执行任务后都会进行保存。

Trigger自身也可以拥有一个JobDataMap其关联的Job可以通过JobExecutionContext#getTrigger()
.getJobDataMap()获取Trigger中的JobDataMap。不管是有状态还是无状态的任务,在任务执行期间对Trigger的JobDataMap所做的更改都不会进行持久,也即不会对下次的执行产生影响。


(3)JobDetail(任务描述)

表示一个具体的可执行的调度程序。ob是这个可执行程调度程序所要执行的内容,另外JobDetail还包含了这个任务调度的方案和策略
Quartz在每次执行Job时,都重新创建一个Job实例,所以它不直接接受一个Job的实例,相反它接收一个Job实现类,以便运行时通过newInstance()的反射机制实例化Job。因此需要通过一个类来描述Job的实现类及其它相关的静态信息,如Job名字、描述、关联监听器等信息,JobDetail承担了这一角色
通过该类的构造函数可以更具体地了解它的功用:JobDetail(java.lang.String name, java.lang.String group, java.lang.Class jobClass),该构造函数要求指定Job的实现类,以及任务在Scheduler中的组名和Job名称

JobDetail对象由Quartz客户端在Job被加入到scheduler时创建。它包含了Job的各种设置属性以及一个JobDataMap对象,这个对象被用来存储给定Job类实例的状态信息。Scheduler并不保存真正的Job Class,而是通过JobDetail来保存。

注意:给scheduler传入了一个JobDetail实例,而且这个JobDetail实例只是简单提供了类名来引用被执行的Job。每次scheduler执行这个任务时,它就创建这个类的新实例,然后调用该实例的execute()。Job不必担心线程安全性,因为同一时刻仅有一个线程去执行给定Job类的实例,甚至是并发执行同一Job也是如此
(1)Job类必须有一个无参数的构造函数
(2)Job类中定义的成员数据失去意义,因为这些成员数据值在每次执行的时候被“清空”了


(4)JobBuilder:定义、创建JobDetail实例


(5)Trigger(触发器)

代表一个调度参数的配置,什么时候去调

1.SimpleTrigger:简单触发

SimpleTrigger用来触发只需执行一次或者在给定时间触发并且重复N次且每次执行延迟一定时间的任务
如果想让触发器在2020年4月25日,上午11: 23:54秒执行,然后每个隔10秒钟重复执行一次,并且这样重复5次,那么SimpleTrigger就可以满足要求

2.CronTrigger:表达式触发

如果需要像日历那样按日程来触发任务,而不是像SimpleTrigger那样每隔特定的间隔时间触发,CronTriggers通常比SimpleTrigger更有用
使用CronTrigger,可以指定诸如[每个周五中午]、或者[每个工作日的9:30]或者[从每个周一、周三、周五的上午9:00到上午10:00之间每隔五分钟]这样日程安排
来触发,甚至,像SimpleTrigger样,CronTrigger也有一个startTime以指定日程从什么时候开始,也有一个(可选的)endTime以指定何时日程不再继续

Quartz Cron表达式
支持7个域,分别是秒/分/时/日/月/周/年。年是非必须项
Cron表达式用来配置CronTrigger实例。Corn表达式是一个由7个表达式组成的字符串。每个表达式都描述了一个单独的日程细节。这些表达式用空格分隔

注意:cron表达式中不区分大小写
Cron表达式对特殊字符的大小写不敏感,对代表星期的缩写英文大小写也不敏感

名称 是否必须 允许值 特殊字符
0-59 , - * /
0-59 , - * /
0-23 , - * /
1-31 , - * ? / L W C
1-7 或 SUN-SAT , - * ? / L C #
1-12 或 JAN-DEC , - * /
空 或 1970-2099 , - * /

星号(*):可用在所有字段中,表示对应时间域的每一个时刻,例如:在分钟字段时,表示“每分钟”
问号(?):该字符只在日期和星期字段中使用,它通常指定为“无意义的值”,相当于点位符;
减号(-):表达一个范围,如在小时字段中使用“10-12”,则表示从10到12点,即10,11,12;
逗号(,):表达一个列表值,如在星期字段中使用“MON,WED,FRI”,则表示星期一,星期三和星期五;
斜杠(/):x/y表达一个等步长序列,x为起始值,y为增量步长值。如在分钟字段中使用0/15,则表示为0,15,30和45秒,而5/15在分钟字段中表示5,20,35,50,你也可以使用*/y,它等同于0/y;
L(每月/星期的最后一天):该字符只在日期和星期字段中使用,代表“Last”的意思,但它在两个字段中意思不同。L在日期字段中,表示这个月份的最后一天,如一月的31号,非闰年二月的28号;如果L用在星期中,则表示星期六,等同于7。但是,如果L出现在星期字段里,而且在前面有一个数值X,则表示“这个月的最后X天”,例如:6L表示该月的最后星期五
W(日期):该字符只能出现在日期字段里,是对前导日期的修饰,表示离该日期最近的工作日。例如15W表示离该月15号最近的工作日,如果该月15号是星期六,则匹配14号星期五;如果15日是星期日,则匹配16号星期一;如果15号是星期二,那结果就是15号星期二。但必须注意关联的匹配日期不能够跨月,如你指定1W,如果1号是星期六,结果匹配的是3号星期一,而非上个月最后的那天。W字符串只能指定单一日期,而不能指定日期范围;
LW组合:在日期字段可以组合使用LW,它的意思是当月的最后一个工作日;
井号(#):该字符只能在星期字段中使用,表示当月某个工作日。如6#3表示当月的第三个星期五(6表示星期五,#3表示当前的第三个),而4#5表示当月的第五个星期三,假设当月没有第五个星期三,忽略不触发;
C:该字符只在日期和星期字段中使用,代表“Calendar”的意思。它的意思是计划所关联的日期,如果日期没有被关联,则相当于日历中所有日期。例如5C在日期字段中就相当于日历5日以后的第一天。1C在星期字段中相当于星期日后的第一天


常用案例

表示式 描述
0 0 12 * * ? 每天12点运行
0 15 10 ? * * 每天10:15运行
0 15 10 * * ? 每天10:15运行
0 15 10 * * ? * 每天10:15运行
0 15 10 * * ? 2008 在2008年的每天10:15运行
0 * 14 * * ? 每天14点到15点之间每分钟运行一次,开始于14:00,结束于14:59
0 0/5 14 * * ? 每天14点到15点每5分钟运行一次,开始于14:00,结束于14:55
0 0/5 14,18 * * ? 每天14点到15点每5分钟运行一次,此外每天18点到19点每5钟也运行一次
0 0-5 14 * * ? 每天14:00点到14:05,每分钟运行一次
0 10,44 14 ? 3 WED 3月每周三的14:10分到14:44,每分钟运行一次
0 15 10 ? * MON-FRI 每周一,二,三,四,五的10:15分运行

(6)TriggerBuilder:定义、创建Trigger实例


(7)Calendar(时间日期包装类)

org.quartz.Calendar和java.util.Calendar不同,它是一些日历特定时间点的集合(可以简单地将org.quartz.Calendar看作java.util.Calendar的集合,代表一个日历时间点)。一个Trigger可以和多个Calendar关联,以便排除或包含某些时间点

假设:安排每周星期一早上10:00执行任务,但是如果碰到法定的节日,任务则不执行,这时就需要在Trigger触发机制的基础上使用 Calendar进行定点排除
针对不同时间段类型,Quartz在org.quartz.impl.calendar包下提供了若干个Calendar 的实现类,如AnnualCalendar、MonthlyCalendar、WeeklyCalendar分别针对每年、每月和每周进行定义


(8)Scheduler(调度器)

代表一个调度容器,一个调度容器中可以注册多个JobDetail和Trigger。当Trigger与JobDetail组合,就可以被Scheduler容器调度了

代表一个Quartz的独立运行容器,Trigger和JobDetail可以注册到Scheduler中,两者在Scheduler中拥有各自的组及名称,组及名称是Scheduler查找定位容器中某一对象的依据,Trigger的组及名称必须唯一,JobDetail的组和名称也必须唯一(但可以和Trigger的组和名称相同,因为它们是不同类型的)。Scheduler定义了多个接口方法,允许外部通过组及名称访问和控制容器中Trigger和JobDetail。

Scheduler可以将Trigger绑定到某一JobDetail中,这样当Trigger触发时,对应的Job就被执行。一个Job可以对应多个Trigger,但一个Trigger只能对应一个Job。可以通过SchedulerFactory创建一个Scheduler实例。 Scheduler拥有一个SchedulerContext,它类似于ServletContext,保存着Scheduler上下文信息,Job和
Trigger都可以访问SchedulerContext内的信息。SchedulerContext内部通过一个Map,以键值对的方式维护这些上下文数据,SchedulerContext为保存和获取数据提供了多个put()和getXxx()的方法。可以通过Scheduler# getContext()获取对应的SchedulerContext实例
Scheduler接口有两个实现类:StdScheduler(标准默认调度器)、RemoteScheduler(远程调度器)


常用方法

方法 描述
shutdown() 关闭定时任务调度器
Date scheduleJob(JobDetail jobDetail,Trigger trigger) 添加一个定时任务
Date rescheduleJob(String triggerName,String groupName, Trigger newTrigger) 修改一个定时任务,主要是更改trigger
boolean deleteJob(String jobName,String jobGroup) 删除一个定时任务,同时也会将于该jobDetail关联的trigger一并删除
String[] getJobGroupNames() 取得所有的jobDetail组
String[] getJobNames(String groupName) 取得某个group下的所有的jobDetail
JobDetail getJobDetail(String jobName,String jobGroup) 取得指定的jobDetail
Trigger[] getTriggersOfJob(String jobName,String groupName) 取得指定的jobDetail的所有的Trigger
Trigger getTrigger(String triggerName,String triggerGroup) 取得指定的Trigger

(9)ThreadPool(线程池)

Scheduler使用一个线程池作为任务运行的基础设施,任务通过共享线程池中的线程提高运行效率


基本使用

1.导入依赖



	org.quartz-scheduler
	quartz
	2.3.2


	com.zaxxer
	HikariCP-java7
	2.4.13


	org.slf4j
	slf4j-api
	1.7.7



	com.mchange
	c3p0
	0.9.5.4


	com.mchange
	mchange-commons-java
	0.2.15

2.创建一个实现Job类的execute(),存放具体的任务

public class HelloJob implements Job {
	public void execute(JobExecutionContext jobExecutionContext) throws JobExecutionException {
		// 打印当前的执行时间
		Date date = new Date();
		SimpleDateFormat sf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
		System.out.println("现在的时间是:" + sf.format(date));
		// 具体的业务逻辑
		System.out.println("Hello Quartz");
	}
}

3.使用SimpleTrigger(简单触发器)、Scheduler(调度器)

public class HelloScheduler {
    public static void main(String[] args) throws SchedulerException {
        //创建一个jobDetail的实例,将该实例与HelloJob Class绑定
        JobDetail jobDetail = JobBuilder.newJob(HelloJob.class)
			.withIdentity("myJob")
			.build();
			
        //创建一个Trigger触发器的实例,定义该job立即执行,并且之后每2秒执行一次,一直执行
        SimpleTrigger trigger = TriggerBuilder.newTrigger()
			.withIdentity("myTrigger")
			.startNow().withSchedule(SimpleScheduleBuilder
			.simpleSchedule()
			.withIntervalInSeconds(2)
			.repeatForever())
			.build();
        //创建schedule实例。调度器
        StdSchedulerFactory factory = new StdSchedulerFactory();
        Scheduler scheduler = factory.getScheduler();
        scheduler.start();
        scheduler.scheduleJob(jobDetail,trigger);
    }
}

Spring中使用

1.导入依赖



	org.springframework
	spring-context-support
	4.3.26.RELEASE


	org.slf4j
	slf4j-log4j12
	1.7.7


	org.quartz-scheduler
	quartz
	2.3.0


	org.quartz-scheduler
	quartz-jobs
	2.3.0
 

2.三种方式

方式一

(1)继承QuartzJobBean,并重写executeInternal(JobExecutionContext context)

//继承QuartzJobBean,并重写executeInternal方法  
public class QuartzTask extends QuartzJobBean {
	private int timeout;
	private static int i = 0;

	// 调度工厂实例化后,经过timeout时间开始执行调度
	public void setTimeout(int timeout) {
		this.timeout = timeout;
	}

	@Override
	protected void executeInternal(JobExecutionContext context) 
throws JobExecutionException {
		System.out.println("task running..." + ++i + "进行中...");
	}
}

(2)配置spring-quratz.xml文件
1.任务调用类;2.任务调用方式;3.任务调用工厂



	
	
	
	
	
		
			
		
	
	
	
	
	
	
	

	


	
	
		
	
	
	
	

	


	
	

	


	
		
			
			
		
	


方式二

(1)不需要继承基类,仍然是POJO

public class QuartzJob {  
    public void work(){  
        System.out.println("work running...");
    }
}

(2)在spring-quratz.xml配置文件中,配置包装类






	
	
		
	
	
	
		
		work
	




	
		
	
	
	
		10,15,20,25,30,35,40,45,50,55 * * * * ?
	




	
		
			
		
	


方式三

(1)配置applicationContext.xml
通过@Scheduled注解的方式实现,需要修改applicationContext.xml三个部分内容
applicationContext.xml中添加task命名空间,然后添加如下代码


(2)使用@Scheduled注解
最后在定时任务上添加上@Scheduled注解即可,一般都采用cronTrigger方式,即@Scheduled(cron=“相应的定时表达式”)

@Service
public class QuartzService {
	@Scheduled(cron = "0/2 * * * * *")
	public void process() {
		System.out.println("job run...");
	}

	public static void main(String[] args) throws InterruptedException {
		ClassPathXmlApplicationContext context = 
new ClassPathXmlApplicationContext("applicationContext.xml");
		while (true) {
			System.out.println("main running..." + context);
			Thread.sleep(10000);
		}
	}
}

Spring Boot整合Quartz任务调度

1.导入依赖



	org.springframework.boot
	spring-boot-starter-quartz


2.实现QuartzJobBean,提供具体任务(DateTimeJob.java,具体的业务逻辑代码)

public class DateTimeJob extends QuartzJobBean {
	@Override
	protected void executeInternal(JobExecutionContext jobExecutionContext) 
throws JobExecutionException {
		// 获取JobDetail中关联的数据
		String msg = (String) jobExecutionContext.getJobDetail()
.getJobDataMap()
.get("msg");
		System.out.println("current time :" + new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date()) + "---" + msg);
	}
}

3.@Configuration配置类(QuartzConfig.java,配置触发器、调度器)

@Configuration
public class QuartzConfig {
	
	// 任务描述,具体可执行的程序
	@Bean
	public JobDetail printTimeJobDetail() {
		return JobBuilder.newJob(DateTimeJob.class) // PrintTimeJob业务类
				.withIdentity("DateTimeJob") // 可以给该JobDetail起一个id
				// 每个JobDetail内都有一个Map,包含了关联到这个Job的数据,在Job类中可以通过context获取
				.usingJobData("msg", "Hello Quartz") // 关联键值对
				.storeDurably() // 即使没有Trigger关联时,也不需要删除该JobDetail
				.build();
	}

    // 触发器关联调度器
	@Bean
	public Trigger printTimeJobTrigger() {
		// 表达式调度器
		CronScheduleBuilder cronScheduleBuilder = CronScheduleBuilder
.cronSchedule("0/1 * * * * ?");
		return TriggerBuilder.newTrigger()
						.forJob(printTimeJobDetail()) // 关联上述的JobDetail
						.withSchedule(cronScheduleBuilder) // 关联触发器
						.withIdentity("quartzTaskService") // 给Trigger(触发器)起个名字
						.build();
	}
}

你可能感兴趣的:(Java定时任务,java,jvm,前端)