文档章节

小议时序调度Timer和Quartz

GreenDay
 GreenDay
发布于 2014/07/21 20:40
字数 1940
阅读 161
收藏 13

本文不是用来讲授入门手把手ABC小例子的,算是自己这段时间对Timer和Quartz使用心得的总结吧,后续如果有更深的认识会不断更新的。 

言归正传,想实现定时调度,最简单的方法是使用Timer 
还是先给个使用范例: 

Java代码  收藏代码

  1. long PERIOD = 60*1000;//一分钟  

  2. Timer timer = new Timer("sure's timer");  

  3. timer.schedule(new TimerTask() {  

  4.     @Override  

  5.     public void run() {  

  6.         if(!doSomething()){  

  7.             timer.cancel();  

  8.         }  

  9.     }  

  10. }, 0, PERIOD);  


上面的代码实现了一个从当前时间开始执行的,以一分钟为周期的定时器。执行的内容在doSomething()方法中实现,这个方法返回是否继续执行的布尔值,因此可以在需要的时候将定时器cancel掉。 

好了,让我们看一看Timer的源码。 
(注:这个本人学习习惯有关,看了一个简单的例子就不想再看大段大段的文字了,还是看看源码比较直接,也比较准确) 

Java代码  收藏代码

  1. public class Timer {   

  2.     private TaskQueue queue = new TaskQueue();  

  3.     private TimerThread thread = new TimerThread(queue);  

  4. }  


Timer主要属性有以上两个,先来看看TimerThread,TimerThread是Timer的内部类,主要代码如下: 

Java代码  收藏代码

  1. class TimerThread extends Thread {  

  2.     private TaskQueue queue;//任务队列  

  3.   

  4.     public void run() {  

  5.         try {  

  6.             mainLoop();  

  7.         } finally {  

  8.             // Someone killed this Thread, behave as if Timer cancelled  

  9.             synchronized(queue) {  

  10.                 newTasksMayBeScheduled = false;  

  11.                 queue.clear();    

  12.             }  

  13.         }  

  14.     }  

  15.   

  16. private void mainLoop() {  

  17.         while (true) {  

  18.             try {  

  19.                 TimerTask task;  

  20.                 boolean taskFired;  

  21.                 synchronized(queue) {  

  22.                     // 如果队列为空则等待  

  23.                     while (queue.isEmpty() && newTasksMayBeScheduled)  

  24.                         queue.wait();  

  25.                     if (queue.isEmpty())  

  26.                         break// 如果不再会非空,则跳出  

  27.   

  28.                     // 队列非空,则取第一个任务执行  

  29.                     long currentTime, executionTime;  

  30.                     task = queue.getMin();//取第一个任务  

  31.                     synchronized(task.lock) {  

  32.                         if (task.state == TimerTask.CANCELLED) {  

  33.                             queue.removeMin();  

  34.                             continue;  // 任务已取消则继续取任务  

  35.                         }  

  36.                         currentTime = System.currentTimeMillis();//当前时间  

  37.                         executionTime = task.nextExecutionTime;//任务将开始执行的时间  

  38.                         if (taskFired = (executionTime<=currentTime)) {  

  39.                             if (task.period == 0) {   

  40.                                 queue.removeMin();  

  41.                                 task.state = TimerTask.EXECUTED;//已执行完成  

  42.                             } else { //重复执行(***)  

  43.                                 queue.rescheduleMin(  

  44.                                   task.period<0 ? currentTime   - task.period  

  45.                                                 : executionTime + task.period);  

  46.                             }  

  47.                         }  

  48.                     }  

  49.                     if (!taskFired) // 还没到时间,则等到执行开始时间  

  50.                         queue.wait(executionTime - currentTime);  

  51.                 }  

  52.                 if (taskFired)  // 执行  

  53.                     task.run();  

  54.             } catch(InterruptedException e) {  

  55.             }  

  56.         }  

  57.     }  

  58. }  


可以看到这代码不亏是出自大师手笔啊考虑的非常详细周到。 
从这个代码可以看出, 
(1)首先,TimerThread是个线程 
(2)TimerThread自己维护了一个任务队列,也就是TaskQueue 
(3)对队列的操作是线程安全的 
(4)***处代码说明很重要的一点:如果前一个任务在执行完的时间已经超过了当前任务原定的开始时间,将当前任务的开始时间重新设置一个值然后执行。这同时也从侧面说明了,定时器的周期并不能结束正在执行的任务。 
这话比较拗口,这样说吧,如果定时器的周期是1分钟,但是任务A执行时间是1分零十秒,当到达1分钟时,本来应该执行任务B,但是这时任务A还未执行完,这时会将任务A执行完,然后在1分钟零十秒的时候重新计算任务B的开始执行时间,设为2分钟时,那么任务B会在2分钟时开始执行。 

在来看看TaskQueue,TaskQueue也是Timer的一个内部类: 

Java代码  收藏代码

  1. class TaskQueue {  

  2.     private TimerTask[] queue = new TimerTask[128];  

  3.     ...  

  4. }  


通过源码可以看到TaskQueue其实就是在维护一个TimerTask[]。 
那TimerTask又是什么呢?就是我们要定时的任务。部分源码如下: 

Java代码  收藏代码

  1. public abstract class TimerTask implements Runnable {  

  2.     final Object lock = new Object();//锁  

  3.   

  4.     public abstract void run();  

  5.   

  6.     public boolean cancel() {  

  7.         synchronized(lock) {  

  8.             boolean result = (state == SCHEDULED);  

  9.             state = CANCELLED;  

  10.             return result;  

  11.         }  

  12.     }  

  13. }  


我们所要做的第一步,就是实现一个TimerTask的对象。 
然后所要做的就是,将这个对象作为参数传入Timer的schedule方法,请看源码: 

Java代码  收藏代码

  1. public void schedule(TimerTask task, long delay, long period) {  

  2.         if (delay < 0)  

  3.             throw new IllegalArgumentException("Negative delay.");  

  4.         if (period <= 0)  

  5.             throw new IllegalArgumentException("Non-positive period.");  

  6.         sched(task, System.currentTimeMillis()+delay, -period);  

  7.     }  

  8.   

  9. private void sched(TimerTask task, long time, long period) {  

  10.         if (time < 0)  

  11.             throw new IllegalArgumentException("Illegal execution time.");  

  12.   

  13.         synchronized(queue) {  

  14.             if (!thread.newTasksMayBeScheduled)  

  15.                 throw new IllegalStateException("Timer already cancelled.");  

  16.   

  17.             synchronized(task.lock) {  

  18.                 if (task.state != TimerTask.VIRGIN)  

  19.                     throw new IllegalStateException(  

  20.                         "Task already scheduled or cancelled");  

  21.                 task.nextExecutionTime = time;//设置该任务的开始时间  

  22.                 task.period = period;//设置该任务的周期  

  23.                 task.state = TimerTask.SCHEDULED;//将该任务的状态设为SCHEDULED  

  24.             }  

  25.   

  26.             queue.add(task);//将该任务加入到任务队列中  

  27.             if (queue.getMin() == task)  

  28.                 queue.notify();//当列队的第一个任务是该任务时,唤醒  

  29.         }  

  30.     }  


再看看我们是如何使用Timer帮助我们实现定时调度的,我们可以发现Timer封装的很好,作为使用者可以不用关注TimerThread是如果对TaskQueue中的一个个TimerTask进行调度的。我们只需要创建一个定时任务然后交给TImer管理即可。但是了解了Timer的内部实现之后,使用起来就更加得心应手了。 

------------------------------------我是分割线--------------------------------------- 
下面再来说说Quartz,Quartz提供了比Timer更加强大的时序调度功能。 
关于Quartz,我不想说太多,原因在于:Quartz官方提供的15个例子太经典啦!!!还在网上找神马乱七八糟的例子啊,这15个例子看看自己跑跑试试,学起来又快又轻松!!!本文附件附送这些例子啊!!! 
为了再降低一下看代码的门槛,这里提供一些关键的概念性的描述,有所了解或者不想看文字的朋友请略过。或者看代码有不懂的地方在来查看也可。 

  • Job:接口,只有一个方法void execute(JobExecutionContext context),开发者实现该接口定义Job所运行的任务,JobExecutionContext类提供了调度上下文的各种信息。Job运行时的信息保存在JobDataMap实例中。

  • 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名称

  • Trigger:是一个类,描述触发Job执行的时间触发规则。主要有SimpleTrigger和CronTrigger这两个子类。当仅需触发一次或者以固定时间间隔周期执行,SimpleTrigger是最适合的选择;而CronTrigger则可以通过Cron表达式定义出各种复杂时间规则的调度方案:如每早晨9:00执行,周一、周三、周五下午5:00执行等

  • Calendar:org.quartz.Calendar和java.util.Calendar不同,它是一些日历特定时间点的集合。一个Trigger可以和多个Calendar关联,以便排除或包含某些时间点

  • Scheduler:Trigger和JobDetail可以注册到Scheduler中,两者在Scheduler中拥有各自的组及名称,组及名称是Scheduler查找定位容器中某一对象的依据。Scheduler可以将Trigger绑定到某一JobDetail中,这样当Trigger触发时,对应的Job就被执行。一个Job可以对应多个Trigger,但一个Trigger只能对应一个Job。可以通过SchedulerFactory创建一个Scheduler实例。Scheduler拥有一个SchedulerContext,它类似于ServletContext,保存着Scheduler上下文信息,Job和Trigger都可以访问SchedulerContext内的信息。

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


更详细的内容可以参考http://www.blogjava.net/baoyaer/articles/155645.html 
在这列出再多例子都没有附件中的例子好,所以就不费口舌了。 


本文转载自:http://hellosure.iteye.com/blog/1135175

GreenDay
粉丝 7
博文 21
码字总数 4972
作品 0
闵行
程序员
私信 提问
Java定时任务调度详解

前言 在实际项目开发中,除了Web应用、SOA服务外,还有一类不可缺少的,那就是定时任务调度。定时任务的场景可以说非常广泛,比如某些视频网站,购买会员后,每天会给会员送成长值,每月会给...

张丰哲
2017/09/24
0
0
Spring 3 调度器示例 —— JDK 定时器和 Quartz 展示

Spring框架提供了执行和调度任务的抽象,支持线程池或者在应用服务器环境中代理给CommonJ. Spring也集成了支持使用JDK Timer和Quartz调度库提供的Quartz Scheduler来实现任务调度的类.两种调度...

oschina
2013/10/21
8.6K
12
几种任务调度的Java实现方法与比较

任务调度是指基于给定时间点,给定时间间隔或者给定执行次数自动执行任务。本文由浅入深介绍四种任务调度的 Java 实现:(1)Timer(2)ScheduledExecutor(3)开源工具包 Quartz(4)开源工...

陶邦仁
2015/03/17
410
0
几种任务调度的 Java 实现方法与比较1——阅读

综观目前的 Web 应用,多数应用都具备任务调度的功能。本文由浅入深介绍了几种任务调度的 Java 实现方法,包括 Timer,Scheduler, Quartz 以及 JCron Tab,并对其优缺点进行比较,目的在于给...

关河
2016/01/29
26
0
几种任务调度的 Java 实现方法与比较

综观目前的 Web 应用,多数应用都具备任务调度的功能。本文由浅入深介绍了几种任务调度的 Java 实现方法,包括 Timer,Scheduler, Quartz 以及 JCron Tab,并对其优缺点进行比较,目的在于给...

IBMdW
2011/09/21
46.1K
24

没有更多内容

加载失败,请刷新页面

加载更多

nginx访问日志/日志切割/静态文件过期时间/防盗链

Nginx访问日志主要记录部署在nginx上的网站访问数据,日志格式定义在nginx主配置文件中。 nginx主配置文件:/etc/nginx/nginx.conf 查看nginx主配置文件: ```markup [root@linux ~]# cat /e...

asnfuy
5分钟前
4
0
JS_高程4.变量,作用域和内存问题(2)执行环境及作用域

本文转载于:专业的前端网站➨JS_高程4.变量,作用域和内存问题(2)执行环境及作用域 1.执行环境:执行环境定义了变量或函数有权访问的其他数据,决定了它们各自的行为,       每个执...

前端老手
9分钟前
3
0
手机迅雷下载的文件,电脑上找不到问题

不知道手机迅雷怎么做到的,手机迅雷下载的文件,手机上可以看的到 但连电脑后, 电脑上看不到文件 . 尝试了打开查看隐藏文件, 但没有效果 . 手机上查询文件权限 也是对的 . 可读,可写, 不隐藏 ...

ol_O_O_lo
11分钟前
3
0
python学习11:Python tuple元组详解

元组是 Python 中另一个重要的序列结构,和列表类似,也是由一系列按特定顺序排序的元素组成。和列表不同的是,列表可以任意操作元素,是可变序列;而元组是不可变序列,即元组中的元素不可以...

太空堡垒185
23分钟前
3
0
Java实现数据结构之线性结构

一、顺序表 顺序表本质是使用数组储存数组的一种数据结构,在计算机的储存中是连续的分配内存的。 下面是我自己使用java实现的简单顺序表结构 package list; public class MyArrayList<E> { ...

daxiongdi
25分钟前
5
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部