在JDK 1.3以后的版本中,Java通過java.util.Timer和java.util.TimerTask這兩個(gè)類提供了簡單的任務(wù)調(diào)度的功能,我們稱之為JDK Timer。
JDK Timer允許按照固定頻率重復(fù)執(zhí)行某項(xiàng)任務(wù),這比直接通過編寫底層線程程序進(jìn)行任務(wù)調(diào)度要輕松許多,但是對于諸如“在每周一10:00執(zhí)行任務(wù)”這種日歷相關(guān)的任務(wù)調(diào)度需求來說,JDK Timer就難以應(yīng)付了。
此外,JDK Timer只適合對執(zhí)行時(shí)間非常短的任務(wù)進(jìn)行調(diào)度,因?yàn)樵赥imer中所有TimerTask都在同一背景線程中執(zhí)行,長時(shí)間的任務(wù)會(huì)嚴(yán)重影響到Timer的調(diào)度工作。
TimerTask代表一個(gè)需要多次執(zhí)行的任務(wù),它實(shí)現(xiàn)了Runnable接口,可以在run()方法中定義任務(wù)邏輯。而Timer負(fù)責(zé)制定調(diào)度規(guī)則并負(fù)責(zé)調(diào)度TimerTask。
TimerTask相當(dāng)于Quartz中Job,代表一個(gè)被調(diào)度的任務(wù)。兩者最主要區(qū)別在于:每當(dāng)執(zhí)行任務(wù)時(shí),Quartz都會(huì)創(chuàng)建一個(gè)Job實(shí)例,而JDK Timer則使用相同的TimerTask實(shí)例。所以如果TimerTask類中擁有狀態(tài),則這些狀態(tài)對于后面的執(zhí)行是可見的,從這點(diǎn)上來說,TimerTask更象是StatefulJob而非Job。TimerTask實(shí)現(xiàn)了Runnable接口,是一個(gè)抽象類,它只有以下3個(gè)方法:
abstract void run():子類覆蓋這個(gè)方法并定義任務(wù)運(yùn)行的邏輯,每次執(zhí)行任務(wù)時(shí),run()方法被執(zhí)行一次;
boolean cancel():取消任務(wù)。如果任務(wù)被安排運(yùn)行一次且任務(wù)未執(zhí)行時(shí),任務(wù)將永遠(yuǎn)不會(huì)運(yùn)行;如果任務(wù)被安排執(zhí)行多次,調(diào)用該方法后,將取消后面的執(zhí)行安排;
long scheduledExecutionTime():返回此任務(wù)最近實(shí)際執(zhí)行的安排執(zhí)行時(shí)間。如果在任務(wù)執(zhí)行過程中調(diào)用此方法,則返回值此次執(zhí)行對應(yīng)的安排執(zhí)行時(shí)間(一個(gè)任務(wù)的實(shí)現(xiàn)執(zhí)行時(shí)間和安排的計(jì)劃執(zhí)行時(shí)間并不一致)。該方法一般在run()方法內(nèi)調(diào)用,你可以通過該方法判斷本次執(zhí)行的時(shí)間點(diǎn)是否過晚,并據(jù)此決定是否要取消本次的運(yùn)行。該方法一般在固定頻率執(zhí)行時(shí)使用才會(huì)有意義。
Timer只能以這樣的方式對任務(wù)進(jìn)行調(diào)度:在延遲一段時(shí)間或在指定時(shí)間點(diǎn)后運(yùn)行一次任務(wù)或周期性的運(yùn)行任務(wù)。實(shí)際上,Timer內(nèi)部使用Object#wait(long time)進(jìn)行任務(wù)的時(shí)間調(diào)度,這種機(jī)制不能保證任務(wù)的實(shí)時(shí)執(zhí)行,只是一個(gè)粗略的近似值。
當(dāng)Timer中所有的TimerTask已經(jīng)執(zhí)行完成并且Timer對象沒有外部引用時(shí),Timer的任務(wù)執(zhí)行線程才會(huì)結(jié)束。但這可能需要很長的時(shí)間才會(huì)發(fā)生。
因此Timer在默認(rèn)情況下使用非后臺(tái)線程(daemon Thread),這樣你就可以在應(yīng)用程序中通過Timer#cancel()方法手工結(jié)束Timer。如果希望盡快結(jié)束Timer中的任務(wù),則可以調(diào)用TimerTask#cancel()方法來達(dá)到目的。
Timer的構(gòu)造函數(shù)在創(chuàng)建Timer對象的同時(shí),將啟動(dòng)一個(gè)Timer背景線程,我們先來了解一下Timer的幾個(gè)構(gòu)造函數(shù):
●Timer():創(chuàng)建一個(gè)Timer,背景線程是非后臺(tái)線程;
●Timer(boolean isDaemon):創(chuàng)建一個(gè)Timer,背景線程是后臺(tái)線程,后臺(tái)線程將在應(yīng)用程序主線程停止后自動(dòng)退出,該方法是JDK 5.0新增的;
●Timer(String name):和Timer()類似,只不過通過name為關(guān)聯(lián)背景線程指定了名稱;
●Timer(String name, boolean isDaemon):和Timer(boolean isDaemon)類似并為關(guān)聯(lián)背景線程指定名稱,該方法是JDK 5.0新增的。
通過以下方法運(yùn)行一次任務(wù):
●schedule(TimerTask task, Date time):在特定時(shí)間點(diǎn)執(zhí)行一次任務(wù);
●schedule(TimerTask task, long delay):延遲指定時(shí)間后,執(zhí)行一次任務(wù),delay的單位為毫秒。
通過以下方法按固定時(shí)間間隔周期性運(yùn)行任務(wù),任務(wù)的執(zhí)行可能產(chǎn)生時(shí)間的漂移:
●schedule(TimerTask task, Date firstTime, long period):從指定時(shí)間點(diǎn)開始周期性執(zhí)行任務(wù),period的單位為毫秒,后一次的執(zhí)行將在前次執(zhí)行完成后才開始計(jì)時(shí)。如任務(wù)被安排2秒鐘運(yùn)行一次,當(dāng)?shù)谝淮芜\(yùn)行花費(fèi)了1.5秒時(shí),第二次將在3.5秒時(shí)運(yùn)行。所以這種任務(wù)調(diào)度是固定時(shí)間間隔;
●schedule(TimerTask task, long delay, long period):延遲指定時(shí)間后,周期性執(zhí)行任務(wù)。
通過以下方法按固定時(shí)間間隔運(yùn)行任務(wù):
●scheduleAtFixedRate(TimerTask task, Date firstTime, long period):在指定時(shí)間點(diǎn)后,以指定頻率運(yùn)行任務(wù)。假設(shè)一個(gè)任務(wù)被安排2秒鐘運(yùn)行一次,如果第一次運(yùn)行花費(fèi)了1.5秒,則在0.5秒后,第二次任務(wù)又會(huì)開始執(zhí)行,以保證固定的執(zhí)行頻率。
●scheduleAtFixedRate(TimerTask task, long delay, long period):在延遲一段時(shí)間后以指定頻率運(yùn)行任務(wù)。
此外,Timer還擁有幾個(gè)控制方法:
●cancel():取消Timer的,并丟棄所有被調(diào)度的TimerTask,不過正在執(zhí)行的任務(wù)不受影響。Timer被取消后,不能調(diào)度新的TimerTask;
聯(lián)系客服