第一步:引包(Maven)

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

第二步:创建要被定执行的任务类

  这一步也很简单,只需要创建一个实现了org.quartz.Job接口的类,并实现这个接口的唯一一个方法execute(JobExecutionContext arg0) throws JobExecutionException即可。如:

package com.fync.quartz;

import java.text.SimpleDateFormat;
import java.util.Date; import org.quartz.Job;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;
/**
* 创建要被定执行的任务类
* @author long
*
*/
public class MyJob implements Job{ public void execute(JobExecutionContext context)
throws JobExecutionException {
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss");
System.out.println(sdf.format(new Date()));
} }

第三步:创建任务调度,并执行

package com.fync.quartz;

import org.quartz.JobBuilder;
import org.quartz.JobDetail;
import org.quartz.Scheduler;
import org.quartz.SchedulerException;
import org.quartz.SchedulerFactory;
import org.quartz.SimpleScheduleBuilder;
import org.quartz.Trigger;
import org.quartz.TriggerBuilder;
import org.quartz.impl.StdSchedulerFactory; /**
* 创建任务调度,并执行
* @author long
*
*/
public class MainScheduler { //创建调度器
public static Scheduler getScheduler() throws SchedulerException{
SchedulerFactory schedulerFactory = new StdSchedulerFactory();
return schedulerFactory.getScheduler();
} public static void schedulerJob() throws SchedulerException{
//创建任务
JobDetail jobDetail = JobBuilder.newJob(MyJob.class).withIdentity("job1", "group1").build();
//创建触发器 每3秒钟执行一次
Trigger trigger = TriggerBuilder.newTrigger().withIdentity("trigger1", "group3")
.withSchedule(SimpleScheduleBuilder.simpleSchedule().withIntervalInSeconds(3).repeatForever())
.build();
Scheduler scheduler = getScheduler();
//将任务及其触发器放入调度器
scheduler.scheduleJob(jobDetail, trigger);
//调度器开始调度任务
scheduler.start(); } public static void main(String[] args) throws SchedulerException {
MainScheduler mainScheduler = new MainScheduler();
mainScheduler.schedulerJob();
} }
MainScheduler.main()依次创建了scheduler(调度器)、job(任务)、trigger(触发器),其中,job指定了MyJob,trigger保存job的触发执行策略(每隔3s执行一次),scheduler将job和trigger绑定在一起,最后scheduler.start()启动调度,每隔3s触发执行JobImpl.execute()
打印出当前时间。 除了SimpleScheduler之外,常用的还有CronTrigger. 原理分析:

1、job(任务)

job由若干个classinterface实现。

Job接口

开发者想要job完成什么样的功能,必须且只能由开发者自己动手来编写实现,比如demo中的MyJob,这点无容置疑。但要想让自己的job被quartz识别,就必须按照quartz的规则来办事,这个规则就是job实现类必须实现Job接口,比如MyJob就实现了Job

Job只有一个execute(JobExecutionContext)JobExecutionContext保存了job的上下文信息,比如绑定的是哪个trigger。job实现类必须重写execute(),执行job实际上就是运行execute()

2、JobDetailImpl类 / JobDetail接口

JobDetailImpl类实现了JobDetail接口,用来描述一个job,定义了job所有属性及其get/set方法。了解job拥有哪些属性,就能知道quartz能提供什么样的能力,下面用表格列出job若干核心属性。

属性名 说明
class 必须是job实现类(比如JobImpl),用来绑定一个具体job
name job名称。如果未指定,会自动分配一个唯一名称。所有job都必须拥有一个唯一name,如果两个job的name重复,则只有最前面的job能被调度
group job所属的组名
description job描述
durability 是否持久化。如果job设置为非持久,当没有活跃的trigger与之关联的时候,job会自动从scheduler中删除。也就是说,非持久job的生命期是由trigger的存在与否决定的
shouldRecover 是否可恢复。如果job设置为可恢复,一旦job执行时scheduler发生hard shutdown(比如进程崩溃或关机),当scheduler重启后,该job会被重新执行
jobDataMap 除了上面常规属性外,用户可以把任意kv数据存入jobDataMap,实现job属性的无限制扩展,执行job时可以使用这些属性数据。此属性的类型是JobDataMap,实现了Serializable接口,可做跨平台的序列化传输

 

3、JobBuilder类

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

上面代码是demo一个片段,可以看出JobBuilder类的作用:接收job实现类MyJob,生成JobDetail实例,默认生成JobDetailImpl实例。

这里运用了建造者模式:JobImpl相当于Product;JobDetail相当于Builder,拥有job的各种属性及其get/set方法;JobBuilder相当于Director,可为一个job组装各种属性。

4、trigger(触发器)

trigger由若干个classinterface实现。

SimpleTriggerImpl类 / SimpleTrigger接口 / Trigger接口

SimpleTriggerImpl类实现了SimpleTrigger接口SimpleTrigger接口继承了Trigger接口,它们表示触发器,用来保存触发job的策略,比如每隔几秒触发job。实际上,quartz有两大触发器:SimpleTriggerCronTrigger,限于篇幅,本文仅介绍SimpleTrigger

Trigger诸类保存了trigger所有属性,同job属性一样,了解trigger属性有助于我们了解quartz能提供什么样的能力,下面用表格列出trigger若干核心属性。

在quartz源码或注释中,经常使用fire(点火)这个动词来命名属性名,表示触发job。

属性名 属性类型 说明
name 所有trigger通用 trigger名称
group 所有trigger通用 trigger所属的组名
description 所有trigger通用 trigger描述
calendarName 所有trigger通用 日历名称,指定使用哪个Calendar类,经常用来从trigger的调度计划中排除某些时间段
misfireInstruction 所有trigger通用 错过job(未在指定时间执行的job)的处理策略,默认为MISFIRE_INSTRUCTION_SMART_POLICY。详见这篇blog[5]
priority 所有trigger通用 优先级,默认为5。当多个trigger同时触发job时,线程池可能不够用,此时根据优先级来决定谁先触发
jobDataMap 所有trigger通用 同job的jobDataMap。假如job和trigger的jobDataMap有同名key,通过getMergedJobDataMap()获取的jobDataMap,将以trigger的为准
startTime 所有trigger通用 触发开始时间,默认为当前时间。决定什么时间开始触发job
endTime 所有trigger通用 触发结束时间。决定什么时间停止触发job
nextFireTime SimpleTrigger私有 下一次触发job的时间
previousFireTime SimpleTrigger私有 上一次触发job的时间
repeatCount SimpleTrigger私有 需触发的总次数
timesTriggered SimpleTrigger私有 已经触发过的次数
repeatInterval SimpleTrigger私有 触发间隔时间

5、TriggerBuilder类

Trigger trigger = TriggerBuilder.newTrigger().withIdentity("trigger1", "group3")
.withSchedule(SimpleScheduleBuilder.simpleSchedule().withIntervalInSeconds(3).repeatForever())
.build();

上面代码是demo一个片段,可以看出TriggerBuilder类的作用:生成Trigger实例,默认生成SimpleTriggerImpl实例。同JobBuilder一样,这里也运用了建造者模式。

6、scheduler(调度器)

scheduler主要由StdScheduler类Scheduler接口StdSchedulerFactory类SchedulerFactory接口QuartzScheduler类实现,它们的关系见下面UML图。

scheduler UML图
// 创建调度器
SchedulerFactory schedulerFactory = new StdSchedulerFactory();
Scheduler scheduler = schedulerFactory.getScheduler();
......
// 将任务及其触发器放入调度器
scheduler.scheduleJob(jobDetail, trigger);
// 调度器开始调度任务
scheduler.start();

上面代码是demo一个片段,可以看出这里运用了工厂模式,通过factory类(StdSchedulerFactory)生产出scheduler实例(StdScheduler)。scheduler是整个quartz的关键,为此,笔者把demo中用到的scheduler接口的源码加上中文注释做个讲解。

  • StdSchedulerFactory.getScheduler()源码
public Scheduler getScheduler() throws SchedulerException {
// 读取quartz配置文件,未指定则顺序遍历各个path下的quartz.properties文件
// 解析出quartz配置内容和环境变量,存入PropertiesParser对象
// PropertiesParser组合了Properties(继承Hashtable),定义了一系列对Properties的操作方法,比如getPropertyGroup()批量获取相同前缀的配置。配置内容和环境变量存放在Properties成员变量中
if (cfg == null) {
initialize();
}
// 获取调度器池,采用了单例模式
// 其实,调度器池的核心变量就是一个hashmap,每个元素key是scheduler名,value是scheduler实例
// getInstance()用synchronized防止并发创建
SchedulerRepository schedRep = SchedulerRepository.getInstance(); // 从调度器池中取出当前配置所用的调度器
Scheduler sched = schedRep.lookup(getSchedulerName()); ...... // 如果调度器池中没有当前配置的调度器,则实例化一个调度器,主要动作包括:
// 1)初始化threadPool(线程池):开发者可以通过org.quartz.threadPool.class配置指定使用哪个线程池类,比如SimpleThreadPool。先class load线程池类,接着动态生成线程池实例bean,然后通过反射,
     //   使用setXXX()方法将以org.quartz.threadPool开头的配置内容赋值给bean成员变量;
// 2)初始化jobStore(任务存储方式):开发者可以通过org.quartz.jobStore.class配置指定使用哪个任务存储类,比如RAMJobStore。先class load任务存储类,接着动态生成实例bean,然后通过反射,
    //   使用setXXX()方法将以org.quartz.jobStore开头的配置内容赋值给bean成员变量;
// 3)初始化dataSource(数据源):开发者可以通过org.quartz.dataSource配置指定数据源详情,比如哪个数据库、账号、密码等。jobStore要指定为JDBCJobStore,dataSource才会有效;
// 4)初始化其他配置:包括SchedulerPlugins、JobListeners、TriggerListeners等;
// 5)初始化threadExecutor(线程执行器):默认为DefaultThreadExecutor;
// 6)创建工作线程:根据配置创建N个工作thread,执行start()启动thread,并将N个thread顺序add进threadPool实例的空闲线程列表availWorkers中;
// 7)创建调度器线程:创建QuartzSchedulerThread实例,并通过threadExecutor.execute(实例)启动调度器线程;
// 8)创建调度器:创建StdScheduler实例,将上面所有配置和引用组合进实例中,并将实例存入调度器池中
sched = instantiate(); return sched;
}

上面有个过程是初始化jobStore,表示使用哪种方式存储scheduler相关数据。quartz有两大jobStore:RAMJobStoreJDBCJobStoreRAMJobStore把数据存入内存,性能最高,配置也简单,但缺点是系统挂了难以恢复数据。JDBCJobStore保存数据到数据库,保证数据的可恢复性,但性能较差且配置复杂。

两种常见的任务存储方式
  • QuartzScheduler.scheduleJob(JobDetail, Trigger)源码
 public Date scheduleJob(JobDetail jobDetail,
Trigger trigger) throws SchedulerException {
// 检查调度器是否开启,如果关闭则throw异常到上层
validateState();
......
// 获取trigger首次触发job的时间,以此时间为起点,每隔一段指定的时间触发job
Date ft = trig.computeFirstFireTime(cal); if (ft == null) {
throw new SchedulerException(
"Based on configured schedule, the given trigger '" + trigger.getKey() + "' will never fire.");
} // 把job和trigger注册进调度器的jobStore
resources.getJobStore().storeJobAndTrigger(jobDetail, trig);
// 通知job监听者
notifySchedulerListenersJobAdded(jobDetail);
// 通知调度器线程
notifySchedulerThread(trigger.getNextFireTime().getTime());
// 通知trigger监听者
notifySchedulerListenersSchduled(trigger); return ft;
}
  • QuartzScheduler.start()源码
public void start() throws SchedulerException {
......
// 这句最关键,作用是使调度器线程跳出一个无限循环,开始轮询所有trigger触发job
// 原理详见“如何采用多线程进行任务调度”
schedThread.togglePause(false);
......
}
												

java--Quartz 定时执行的更多相关文章

  1. 关于sping quartz定时执行理解与思考

    转载请注明原创出处,谢谢! 一直以为自己理解spring quartz,忽然最近几天发现自己理解的不对,在4月18号的时候,我执行了一个spring quartz的计划如下: 1 0 0 */3 * ...

  2. java中定时执行任务

    现在项目中用到需要定时去检查文件是否更新的功能.timer正好用于此处. 用法很简单,new一个timer,然后写一个timertask的子类即可. 代码如下: package comz.autoup ...

  3. 用Quartz处理定时执行的任务

    这次做的项目中,有一部分功能需要实现定时执行.呃,这样说可能有点笼统,打个比方吧.例如用户在登录的时候,连续输错3次密码后,系统会将该用户冻结,不再允许该用户登录系统,等到了晚上零晨时分,再为所有被冻 ...

  4. java 多线程——quartz 定时调度的例子

    java 多线程 目录: Java 多线程——基础知识 Java 多线程 —— synchronized关键字 java 多线程——一个定时调度的例子 java 多线程——quartz 定时调度的例子 ...

  5. java Quartz定时器任务与Spring task定时的几种实现,

    java Quartz定时器任务与Spring task定时的几种实现 基于java 的定时任务实现, Quartz 时间详细配置    请查阅   http://www.cnblogs.com/si ...

  6. Quartz.Net实现的定时执行任务调度

    在之前的文章<推荐一个简单.轻量.功能非常强大的C#/ASP.NET定时任务执行管理器组件–FluentScheduler>和<简单.轻量.功能非常强大的C#/ASP.NET定时调度 ...

  7. Quartz定时调度jar包的执行Demo分享

    1.Quartz简介 ​ Quartz框架的核心是调度器.调度器负责管理Quartz应用运行时环境.调度器不是靠自己做所有的工作,而是依赖框架内一些非常重要的部件.Quartz不仅仅是线程和线程管理. ...

  8. Java 在某一个时间点定时执行任务(转载)

    java定时任务,每天定时执行任务.以下是这个例子的全部代码. public class TimerManager { //时间间隔 private static final long PERIOD_ ...

  9. JAVA定时执行任务,每天定时几点钟执行任务

    JAVA定时执行任务,每天定时几点钟执行任务的示例如下: 1.建立TimerManage类,设置时间点,时间点设置的管理类,代码如下: package com.pcitc.time; import j ...

  10. java web每天定时执行任务

    第一步: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 ...

随机推荐

  1. webrtc--stun-turn

    .WebRTC后台服务: 通话的房间服务器(Room Server) 房间服务器是用来创建和管理通话会话的状态维护,是双方通话还是多方通话,加入与离开房间等等,我们暂时沿用Google部署在GAE平台 ...

  2. Caocao's Bridges---hdu4738(桥)

    http://acm.hdu.edu.cn/showproblem.php?pid=4738 题就是求出所有的桥,然后输出桥的权值的最小值. 如果一开始是不连通的,输出0. 图有重边,需要处理, 不能 ...

  3. Postgresql 正则表达式(转)

    原文:http://blog.csdn.net/wugewuge/article/details/7704996 postgresql支持POSIX 风格的正则表达式,在postgresql中使用正则 ...

  4. Python多进程编程(转)

    原文:http://www.cnblogs.com/kaituorensheng/p/4445418.html 阅读目录 1. Process 2. Lock 3. Semaphore 4. Even ...

  5. 大话https演化过程(对称加密、非对称加密、公钥、私钥、数字签名、数字证书)

    大话https演化过程(包括概念:对称加密.非对称加密.公钥.私钥.数字签名.数字证书.https访问全过程)   在网络上发送数据是非常不安全的,非常容易被劫持或是被篡改,所以每次定向发送数据你都可 ...

  6. 【Maven学习】Nexus OSS私服仓库的备份与迁移

    背景 在上一篇博客 [Maven学习]Nexus OSS私服仓库的安装和配置 中,我们已经在机房搭建好了新的Nexus OSS私服仓库.下面是两个版本的Nexus OSS私服仓库的对比图. 老的Nex ...

  7. Openstack(十二)部署neuron(计算节点)

    在计算节点安装 12.1安装neuron(计算节点) # yum install openstack-neutron-linuxbridge ebtables ipset –y 12.2配置neutr ...

  8. Python Web开发之路

    Flask相关 1.DBUtils数据库连接池 2.Flask之初体验 3.Flask之WTForms 4.Flask之信号 5.Flask之flask-session 6.Flask之flask-s ...

  9. The adidas NMD Singapore is one of the brands top selling

    Like pointed out, we've two adidas NMD Singapore releases using the first arriving Blue and Black as ...

  10. 安装memcached扩展 验证过了可行

    . 安装libmemached 复制代码 代码如下: wget https://launchpad.net/libmemcached/1.0/1.0.16/+download/libmemcached ...