本文不是用來說授入門手把手ABC小例子的,算是本身這段時間對Timer和Quartz使用心得的總結吧,後續若是有更深的認識會不斷更新的。
言歸正傳,想實現定時調度,最簡單的方法是使用Timer
仍是先給個使用範例:
html
long PERIOD = 60*1000;//一分鐘 安全
Timer timer = new Timer("sure's timer"); ide
timer.schedule(new TimerTask() { 函數
@Override oop
public void run() { 學習
if(!doSomething()){ this
timer.cancel(); spa
} .net
}
}, 0, PERIOD);
上面的代碼實現了一個從當前時間開始執行的,以一分鐘爲週期的定時器。執行的內容在doSomething()方法中實現,這個方法返回是否繼續執行的布爾值,所以能夠在須要的時候將定時器cancel掉。
好了,讓咱們看一看Timer的源碼。
(注:這個本人學習習慣有關,看了一個簡單的例子就不想再看大段大段的文字了,仍是看看源碼比較直接,也比較準確)
public class Timer {
private TaskQueue queue = new TaskQueue();
private TimerThread thread = new TimerThread(queue);
}
Timer主要屬性有以上兩個,先來看看TimerThread,TimerThread是Timer的內部類,主要代碼以下:
class TimerThread extends Thread {
private TaskQueue queue;//任務隊列
public void run() {
try {
mainLoop();
} finally {
// Someone killed this Thread, behave as if Timer cancelled
synchronized(queue) {
newTasksMayBeScheduled = false;
queue.clear();
}
}
}
private void mainLoop() {
while (true) {
try {
TimerTask task;
boolean taskFired;
synchronized(queue) {
// 若是隊列爲空則等待
while (queue.isEmpty() && newTasksMayBeScheduled)
queue.wait();
if (queue.isEmpty())
break; // 若是再也不會非空,則跳出
// 隊列非空,則取第一個任務執行
long currentTime, executionTime;
task = queue.getMin();//取第一個任務
synchronized(task.lock) {
if (task.state == TimerTask.CANCELLED) {
queue.removeMin();
continue; // 任務已取消則繼續取任務
}
currentTime = System.currentTimeMillis();//當前時間
executionTime = task.nextExecutionTime;//任務將開始執行的時間
if (taskFired = (executionTime<=currentTime)) {
if (task.period == 0) {
queue.removeMin();
task.state = TimerTask.EXECUTED;//已執行完成
} else { //重複執行(***)
queue.rescheduleMin(
task.period<0 ? currentTime - task.period
: executionTime + task.period);
}
}
}
if (!taskFired) // 還沒到時間,則等到執行開始時間
queue.wait(executionTime - currentTime);
}
if (taskFired) // 執行
task.run();
} catch(InterruptedException e) {
}
}
}
}
能夠看到這代碼不虧是出自大師手筆啊考慮的很是詳細周到。
從這個代碼能夠看出,
(1)首先,TimerThread是個線程
(2)TimerThread本身維護了一個任務隊列,也就是TaskQueue
(3)對隊列的操做是線程安全的
(4)***處代碼說明很重要的一點:若是前一個任務在執行完的時間已經超過了當前任務原定的開始時間,將當前任務的開始時間從新設置一個值而後執行。這同時也從側面說明了,定時器的週期並不能結束正在執行的任務。
這話比較拗口,這樣說吧,若是定時器的週期是1分鐘,可是任務A執行時間是1分零十秒,當到達1分鐘時,原本應該執行任務B,可是這時任務A還未執行完,這時會將任務A執行完,而後在1分鐘零十秒的時候從新計算任務B的開始執行時間,設爲2分鐘時,那麼任務B會在2分鐘時開始執行。
在來看看TaskQueue,TaskQueue也是Timer的一個內部類:
class TaskQueue {
private TimerTask[] queue = new TimerTask[128];
...
}
經過源碼能夠看到TaskQueue其實就是在維護一個TimerTask[]。
那TimerTask又是什麼呢?就是咱們要定時的任務。部分源碼以下:
public abstract class TimerTask implements Runnable {
final Object lock = new Object();//鎖
public abstract void run();
public boolean cancel() {
synchronized(lock) {
boolean result = (state == SCHEDULED);
state = CANCELLED;
return result;
}
}
}
咱們所要作的第一步,就是實現一個TimerTask的對象。
而後所要作的就是,將這個對象做爲參數傳入Timer的schedule方法,請看源碼:
public void schedule(TimerTask task, long delay, long period) {
if (delay < 0)
throw new IllegalArgumentException("Negative delay.");
if (period <= 0)
throw new IllegalArgumentException("Non-positive period.");
sched(task, System.currentTimeMillis()+delay, -period);
}
private void sched(TimerTask task, long time, long period) {
if (time < 0)
throw new IllegalArgumentException("Illegal execution time.");
synchronized(queue) {
if (!thread.newTasksMayBeScheduled)
throw new IllegalStateException("Timer already cancelled.");
synchronized(task.lock) {
if (task.state != TimerTask.VIRGIN)
throw new IllegalStateException(
"Task already scheduled or cancelled");
task.nextExecutionTime = time;//設置該任務的開始時間
task.period = period;//設置該任務的週期
task.state = TimerTask.SCHEDULED;//將該任務的狀態設爲SCHEDULED
}
queue.add(task);//將該任務加入到任務隊列中
if (queue.getMin() == task)
queue.notify();//當列隊的第一個任務是該任務時,喚醒
}
}
再看看咱們是如何使用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執行,周1、周3、週五下午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
在這列出再多例子都沒有附件中的例子好,因此就不費口舌了。