Timer和TimerTask
Timer就是一個(gè)調(diào)度器蔓同,而TimerTask只是一個(gè)實(shí)現(xiàn)了run方法的類千康,而具體的TimerTask需要由你自己來實(shí)現(xiàn)据悔,例如這樣:
Timer timer = new Timer();
timer.schedule(new TimerTask() {
public void run() {
System.out.println("abc");
}
}, 20000 , 1000);
這里直接實(shí)現(xiàn)一個(gè)TimerTask(當(dāng)然嗅骄,你可以實(shí)現(xiàn)多個(gè)TimerTask,多個(gè)TimerTask可以被一個(gè)Timer調(diào)度客情,后面會說到Timer的內(nèi)部調(diào)度機(jī)制),然后編寫run方法癞己,20s后開始執(zhí)行膀斋,每秒執(zhí)行一次,當(dāng)然你可以通過一個(gè)timer對象來操作多個(gè)timerTask痹雅,其實(shí)timerTask本身沒什么意義仰担,只是timer集合操作的一個(gè)對象,實(shí)現(xiàn)它就必然有對應(yīng)的run方法绩社,以被調(diào)用摔蓝,他甚至于根本不需要實(shí)現(xiàn)Runnable技掏,因?yàn)檫@樣往往混淆視聽了,為什么呢项鬼?也是本文要說的重點(diǎn)哑梳。
在說到timer的原理時(shí),我們先看看Timer里面的一些常見方法:
// 這個(gè)方法是調(diào)度一個(gè)task绘盟,經(jīng)過delay(ms)后開始進(jìn)行調(diào)度鸠真,僅僅調(diào)度一次。
public void schedule(TimerTask task, long delay);
// 在指定的時(shí)間點(diǎn)time上調(diào)度一次龄毡。
public void schedule(TimerTask task, Date time);
// 這個(gè)方法是調(diào)度一個(gè)task吠卷,在delay(ms)后開始調(diào)度,每次調(diào)度完后沦零,最少等待period(ms)后才開始調(diào)度祭隔。
public void schedule(TimerTask task, long delay, long period);
// 和上一個(gè)方法類似,唯一的區(qū)別就是傳入的第二個(gè)參數(shù)為第一次調(diào)度的時(shí)間路操。
public void schedule(TimerTask task, Date firstTime, long period);
/**
調(diào)度一個(gè)task疾渴,在delay(ms)后開始調(diào)度,然后每經(jīng)過period(ms)再次調(diào)度屯仗,貌似和方法:
schedule是一樣的搞坝,其實(shí)不然,后面你會根據(jù)源碼看到魁袜,schedule在計(jì)算下一次執(zhí)行的時(shí)間的時(shí)候桩撮,
是通過當(dāng)前時(shí)間(在任務(wù)執(zhí)行前得到) + 時(shí)間片,而scheduleAtFixedRate方法是通過當(dāng)前需要執(zhí)
行的時(shí)間(也就是計(jì)算出現(xiàn)在應(yīng)該執(zhí)行的時(shí)間)+ 時(shí)間片峰弹,前者是運(yùn)行的實(shí)際時(shí)間店量,而后者是理論時(shí)間
點(diǎn),例如:schedule的時(shí)間片是5s鞠呈,那么理論上會在5融师、10、15粟按、20這些時(shí)間片被調(diào)度诬滩,但是如果由于
某些CPU征用導(dǎo)致未被調(diào)度,假如等到第8s才被第一次調(diào)度灭将,那么schedule方法計(jì)算出來的下一次時(shí)
間應(yīng)該是第13s而不是第10s疼鸟,這樣有可能下次就越到20s后而被少調(diào)度一次或多次,而
scheduleAtFixedRate方法就是每次理論計(jì)算出下一次需要調(diào)度的時(shí)間用以排序庙曙,若第8s被調(diào)度空镜,那
么計(jì)算出應(yīng)該是第10s,所以它距離當(dāng)前時(shí)間是2s,那么在調(diào)度隊(duì)列排序中吴攒,會被優(yōu)先調(diào)度张抄,那么就盡
量減少漏掉調(diào)度的情況。
**/
public void scheduleAtFixedRate(TimerTask task, long delay, long period);
// 方法同上洼怔,唯一的區(qū)別就是第一次調(diào)度時(shí)間設(shè)置為一個(gè)Date時(shí)間署惯,而不是當(dāng)前時(shí)間的一個(gè)時(shí)間片,我們在源碼中會詳細(xì)說明這些內(nèi)容镣隶。
public void scheduleAtFixedRate(TimerTask task, Date firstTime,long period);
構(gòu)造方法
首先看一下Timer的構(gòu)造方法有幾種:
構(gòu)造方法1:無參構(gòu)造方法极谊,簡單通過"Tiemer"為前綴構(gòu)造一個(gè)線程名稱:
public Timer() {
this("Timer-" + serialNumber());
}
創(chuàng)建的線程不為主線程,則主線程結(jié)束后安岂,timer自動(dòng)結(jié)束轻猖,而無需使用cancel來完成對timer的結(jié)束。
構(gòu)造方法2:傳入了是否為后臺線程域那,后臺線程當(dāng)且僅當(dāng)進(jìn)程結(jié)束時(shí)咙边,自動(dòng)注銷掉。
public Timer(boolean isDaemon) {
this("Timer-" + serialNumber(), isDaemon);
}
另外兩個(gè)構(gòu)造方法負(fù)責(zé)傳入名稱和將timer啟動(dòng):
public Timer(String name, boolean isDaemon) {
thread.setName(name);
thread.setDaemon(isDaemon);
thread.start();
}
這里有一個(gè)thread次员,這個(gè)thread很明顯是一個(gè)線程败许,被包裝在了Timer類中,我們看下這個(gè)thread的定義是:
private TimerThread thread = new TimerThread(queue);
而定義TimerThread部分的是:
class TimerThread extends Thread
看到這里知道了翠肘,Timer內(nèi)部包裝了一個(gè)線程檐束,用來做獨(dú)立于外部線程的調(diào)度辫秧,而TimerThread是一個(gè)default類型的束倍,默認(rèn)情況下是引用不到的,是被Timer自己所使用的盟戏。
除了上面提到的thread绪妹,還有一個(gè)很重要的屬性是:
private TaskQueue queue = new TaskQueue();
看名字就知道是一個(gè)隊(duì)列,隊(duì)列里面可以先猜猜看是什么柿究,那么大概應(yīng)該是我要調(diào)度的任務(wù)吧邮旷,先記錄下了,接下來繼續(xù)向下看:
里面還有一個(gè)屬性是:threadReaper蝇摸,它是Object類型婶肩,只是重寫了finalize方法而已,是為了垃圾回收的時(shí)候貌夕,將相應(yīng)的信息回收掉律歼,做GC的回補(bǔ),也就是當(dāng)timer線程由于某種原因死掉了啡专,而未被cancel险毁,里面的隊(duì)列中的信息需要清空掉,不過我們通常是不會考慮這個(gè)方法的,所以知道java寫這個(gè)方法是干什么的就行了畔况。
調(diào)度方法的實(shí)現(xiàn)
對于上面6個(gè)調(diào)度方法鲸鹦,我們不做一一列舉,為什么等下你就知道了:
來看下方法:
public void schedule(TimerTask task, long delay);
的源碼如下:
public void schedule(TimerTask task, long delay) {
if (delay < 0)
throw new IllegalArgumentException("Negative delay.");
sched(task, System.currentTimeMillis()+delay, 0);
}
這里調(diào)用了另一個(gè)方法跷跪,將task傳入馋嗜,第一個(gè)參數(shù)傳入System.currentTimeMillis()+delay可見為第一次需要執(zhí)行的時(shí)間的時(shí)間點(diǎn)了(如果傳入Date,就是對象.getTime()即可吵瞻,所以傳入Date的幾個(gè)方法就不用多說了)嵌戈,而第三個(gè)參數(shù)傳入了0,這里可以猜下要么是時(shí)間片听皿,要么是次數(shù)啥的熟呛,不過等會就知道是什么了;另外關(guān)于方法:sched的內(nèi)容我們不著急去看他尉姨,先看下重載的方法中是如何做的庵朝。
再看看方法:
public void schedule(TimerTask task, long delay,long period);
源碼為:
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);
}
看來也調(diào)用了方法sched來完成調(diào)度,和上面的方法唯一的區(qū)別是增加了傳入的period又厉,而第一個(gè)傳入的是0九府,所以確定這個(gè)參數(shù)為時(shí)間片,而不是次數(shù)覆致,注意這里的period加了一個(gè)負(fù)數(shù)侄旬,也就是取反,也就是我們開始傳入1000煌妈,在調(diào)用sched的時(shí)候會變成-1000儡羔,其實(shí)最終閱讀完源碼后你會發(fā)現(xiàn)這個(gè)算是老外對于一種數(shù)字的理解,而并非有什么特殊的意義璧诵,所以閱讀源碼的時(shí)候也有這些困難所在汰蜘。
最后再看個(gè)方法是:
public void scheduleAtFixedRate(TimerTask task,long delay,long period);
源碼為:
public void scheduleAtFixedRate(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);
}
唯一的區(qū)別就是period沒有取反,其實(shí)你最終閱讀完源碼之宿,上面的取反沒有什么特殊的意義族操,老外不想增加一個(gè)參數(shù)來表示scheduleAtFixedRate,而scheduleAtFixedRate和schedule的大部分邏輯代碼一致比被,因此用了參數(shù)的范圍來作為區(qū)分方法色难。
來看sched方法的實(shí)現(xiàn)體:
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;
}
queue.add(task);
if (queue.getMin() == task)
queue.notify();
}
}
queue為一個(gè)隊(duì)列,我們先不看他的數(shù)據(jù)結(jié)構(gòu)等缀,看到他在做這個(gè)操作的時(shí)候枷莉,發(fā)生了同步,所以在timer級別项滑,這個(gè)是線程安全的依沮,最后將task相關(guān)的參數(shù)賦值涯贞,主要包含nextExecutionTime(下一次執(zhí)行時(shí)間),period(時(shí)間片)危喉,state(狀態(tài))宋渔,然后將它放入queue隊(duì)列中,做一次notify操作辜限,為什么要做notify操作呢皇拣?看了后面的代碼你就知道了。
簡言之薄嫡,這里就是將task放入隊(duì)列queue的過程氧急,此時(shí),你可能對queue的結(jié)構(gòu)有些興趣毫深,那么我們先來看看queue屬性的結(jié)構(gòu)TaskQueue:
class TaskQueue {
private TimerTask[] queue = new TimerTask[128];
private int size = 0;
}
可見吩坝,TaskQueue的結(jié)構(gòu)很簡單,為一個(gè)數(shù)組哑蔫,加一個(gè)size钉寝,有點(diǎn)像ArrayList,是不是長度就128呢闸迷,當(dāng)然不是嵌纲,ArrayList可以擴(kuò)容,它也可以腥沽,只是會造成內(nèi)存拷貝而已逮走,所以一個(gè)Timer來講,只要內(nèi)部的task個(gè)數(shù)不超過128是不會造成擴(kuò)容的今阳;內(nèi)部提供了add(TimerTask)师溅、size()、getMin()酣栈、get(int)险胰、removeMin()、quickRemove(int)矿筝、rescheduleMin(long newTime)、isEmpty()棚贾、clear()窖维、fixUp()、fixDown()妙痹、heapify()等方法铸史。
這里面的方法大概意思是:
add(TimerTask t)為增加一個(gè)任務(wù);
size()任務(wù)隊(duì)列的長度怯伊;
getMin()獲取當(dāng)前排序后最近需要執(zhí)行的一個(gè)任務(wù)琳轿,下標(biāo)為1,隊(duì)列頭部0是不做任何操作的;
get(int i)獲取指定下標(biāo)的數(shù)據(jù)崭篡,當(dāng)然包括下標(biāo)0挪哄;
removeMin()為刪除當(dāng)前最近執(zhí)行的任務(wù),也就是第一個(gè)元素琉闪,通常只調(diào)度一次的任務(wù)迹炼,在執(zhí)行完后,調(diào)用此方法颠毙,就可以將TimerTask從隊(duì)列中移除斯入;
quickRmove(int i)刪除指定的元素,一般來說是不會調(diào)用這個(gè)方法的蛀蜜,這個(gè)方法只有在Timer發(fā)生purge的時(shí)候刻两,并且當(dāng)對應(yīng)的TimerTask調(diào)用了cancel方法的時(shí)候,才會調(diào)用這個(gè)方法滴某,也就是取消某個(gè)TimerTask闹伪,然后就會從隊(duì)列中移除(注意:如果任務(wù)正在執(zhí)行,雖然從隊(duì)列中移除了壮池,仍然會執(zhí)行完這個(gè)任務(wù))偏瓤,還有就是這個(gè)cancel方法并不是Timer的cancel方法而是TimerTask的,一個(gè)是調(diào)度器的椰憋,一個(gè)是單個(gè)任務(wù)的厅克,最后注意,這個(gè)quickRmove完成后橙依,是將隊(duì)列最后一個(gè)元素補(bǔ)充到這個(gè)位置证舟,所以此時(shí)會造成順序不一致的問題,后面會有方法進(jìn)行回補(bǔ)窗骑;
rescheduleMin(long newTime)是重新設(shè)置當(dāng)前執(zhí)行的任務(wù)的下一次執(zhí)行時(shí)間女责,并在隊(duì)列中將其重新排序到合適的位置,而調(diào)用的是后面說的fixDown方法创译;
對于fixUp和fixDown方法來講抵知,前者是當(dāng)新增一個(gè)task的時(shí)候,首先將元素放在隊(duì)列的尾部软族,然后向前找是否有比自己還要晚執(zhí)行的任務(wù)刷喜,如果有,就將兩個(gè)任務(wù)的順序進(jìn)行交換一下立砸。而fixDown正好相反掖疮,執(zhí)行完第一個(gè)任務(wù)后,需要加上一個(gè)時(shí)間片得到下一次執(zhí)行時(shí)間颗祝,從而需要將其順序與后面的任務(wù)進(jìn)行調(diào)整浊闪。
其次可以看下fixDown的細(xì)節(jié)為:
private void fixDown(int k) {
int j;
while ((j = k << 1) <= size && j > 0) {
if (j < size &&
queue[j].nextExecutionTime > queue[j+1].nextExecutionTime)
j++; // j indexes smallest kid
if (queue[k].nextExecutionTime <= queue[j].nextExecutionTime)
break;
TimerTask tmp = queue[j]; queue[j] = queue[k]; queue[k] = tmp;
k = j;
}
}
這種方式并非排序恼布,而是找到一個(gè)合適的位置來交換,因?yàn)椴⒉皇峭ㄟ^隊(duì)列逐個(gè)找的搁宾,而是每次移動(dòng)一個(gè)二進(jìn)制位折汞,例如傳入1的時(shí)候,接下來就是2猛铅、4字支、8、16這些位置奸忽,找到合適的位置放下即可堕伪,順序未必是完全有序的,它只需要看到距離調(diào)度部分的越近的是有序性越強(qiáng)的時(shí)候就可以了栗菜,這樣即可以保證一定的順序性欠雌,達(dá)到較好的性能。
最后一個(gè)方法是heapify疙筹,其實(shí)就是將隊(duì)列的后半截富俄,全部做一次fixeDown的操作,這個(gè)操作主要是為了回補(bǔ)quickRemove方法而咆,當(dāng)大量的quickRmove后霍比,順序被打亂后,此時(shí)將一半的區(qū)域做一次非常簡單的排序即可暴备。
這些方法我們不在說源碼了悠瞬,只需要知道它提供了類似于ArrayList的東西來管理,內(nèi)部有很多排序之類的處理涯捻,我們繼續(xù)回到Timer浅妆,里面還有兩個(gè)方法是:cancel()方法和purge()方法,其實(shí)就cancel方法來講障癌,一個(gè)取消操作凌外,在測試中你會發(fā)現(xiàn),如果一旦執(zhí)行了這個(gè)方法timer就會結(jié)束掉涛浙,看下源碼是什么呢:
public void cancel() {
synchronized(queue) {
thread.newTasksMayBeScheduled = false;
queue.clear();
queue.notify(); // In case queue was already empty.
}
}
貌似僅僅將隊(duì)列清空掉康辑,然后設(shè)置了newTasksMayBeScheduled狀態(tài)為false,最后讓隊(duì)列調(diào)用了notify操作蝗拿,但是沒有任何地方讓線程結(jié)束掉晾捏,那么就要回到我們開始說的Timer中包含的thread為:TimerThread類了,在看這個(gè)類之前哀托,再看下Timer中最后一個(gè)purge()方法,當(dāng)你對很多Task做了cancel操作后劳秋,此時(shí)通過調(diào)用purge方法實(shí)現(xiàn)對這些cancel的任務(wù)從隊(duì)列中移除仓手,上面已經(jīng)提到胖齐,此時(shí)會造成順序混亂,所以需要調(diào)用隊(duì)里的heapify方法來完成順序的重排嗽冒,源碼如下:
public int purge() {
int result = 0;
synchronized(queue) {
for (int i = queue.size(); i > 0; i--) {
if (queue.get(i).state == TimerTask.CANCELLED) {
queue.quickRemove(i);
result++;
}
}
if (result != 0)
queue.heapify();
}
return result;
}
TimerThread類
我們來看看TimerThread類:
class TimerThread extends Thread {
boolean newTasksMayBeScheduled = true;
private TaskQueue queue;
TimerThread(TaskQueue queue) {
this.queue = queue;
}
}
內(nèi)部有一個(gè)屬性是:newTasksMayBeScheduled呀伙,也就是我們開始所提及的那個(gè)在cancel的時(shí)候會被設(shè)置為false的參數(shù)。
另一個(gè)屬性也就是我們所講的任務(wù)隊(duì)列queue了添坊,這下聯(lián)通了吧剿另,不過這里的queue是通過構(gòu)造方法傳入的,很明顯是Timer傳遞給這個(gè)線程的贬蛙,我們知道它是一個(gè)線程雨女,所以執(zhí)行的中心自然是run方法了,所以看下run方法的實(shí)現(xiàn):
public void run() {
try {
mainLoop();
} finally {
// Someone killed this Thread, behave as if Timer cancelled
synchronized(queue) {
newTasksMayBeScheduled = false;
queue.clear(); // Eliminate obsolete references
}
}
}
try很簡單阳准,就一個(gè)mainLoop氛堕,看名字就知道是主循環(huán)程序,finally中也就是必然執(zhí)行的程序:將參數(shù)設(shè)置為false野蝇,并將隊(duì)列清空掉讼稚。
那么最核心的就是mainLoop了,是的绕沈,看懂了mainLoop一切都懂了:
private void mainLoop() {
while (true) {
try {
TimerTask task;
boolean taskFired;
synchronized(queue) {
// Wait for queue to become non-empty
while (queue.isEmpty() && newTasksMayBeScheduled)
queue.wait();
if (queue.isEmpty())
break; // Queue is empty and will forever remain; die
// Queue nonempty; look at first evt and do the right thing
long currentTime, executionTime;
task = queue.getMin();
synchronized(task.lock) {
if (task.state == TimerTask.CANCELLED) {
queue.removeMin();
continue; // No action required, poll queue again
}
currentTime = System.currentTimeMillis();
executionTime = task.nextExecutionTime;
if (taskFired = (executionTime<=currentTime)) {
if (task.period == 0) { // Non-repeating, remove
queue.removeMin();
task.state = TimerTask.EXECUTED;
} else { // Repeating task, reschedule
queue.rescheduleMin(
task.period<0 ? currentTime - task.period
: executionTime + task.period);
}
}
}
if (!taskFired) // Task hasn't yet fired; wait
queue.wait(executionTime - currentTime);
}
if (taskFired) // Task fired; run it, holding no locks
task.run();
} catch(InterruptedException e) {
}
}
}
可以發(fā)現(xiàn)這個(gè)timer是一個(gè)死循環(huán)程序锐想,除非遇到不能捕獲的異常或break才會跳出乍狐,首先注意這段代碼:
while (queue.isEmpty() && newTasksMayBeScheduled)
queue.wait();
循環(huán)體在循環(huán)過程中赠摇,queue為空且newTasksMayBeScheduled狀態(tài)為true,可以看到這個(gè)狀態(tài)的關(guān)鍵作用澜躺,也就是跳出循環(huán)的條件就是:要么隊(duì)列不為空蝉稳,要么是newTasksMayBeScheduled狀態(tài)設(shè)置為false。而wait就是在等待其他地方對queue發(fā)生notify操作掘鄙,從上面的代碼中可以發(fā)現(xiàn)耘戚,當(dāng)發(fā)生add、cancel以及在threadReaper調(diào)用finalize方法的時(shí)候會被調(diào)用操漠,第三個(gè)我們基本可以不考慮收津。發(fā)生add的時(shí)候也就是當(dāng)隊(duì)列還是空的時(shí)候,發(fā)生add使得隊(duì)列不為空就跳出循環(huán)浊伙,而cancel是設(shè)置了狀態(tài)撞秋,否則不會進(jìn)入這個(gè)循環(huán),那么看下面的代碼:
if (queue.isEmpty())
break;
當(dāng)跳出上面的循環(huán)后嚣鄙,如果是設(shè)置了newTasksMayBeScheduled狀態(tài)為false跳出吻贿,也就是調(diào)用了cancel,那么queue就是空的哑子,此時(shí)就直接跳出外部的死循環(huán)舅列,所以cancel就是這樣實(shí)現(xiàn)的肌割,如果下面的任務(wù)還在跑還沒運(yùn)行到這里來,cancel是不起作用的帐要。
接下來是獲取一個(gè)當(dāng)前系統(tǒng)時(shí)間和上次預(yù)計(jì)的執(zhí)行時(shí)間把敞,如果預(yù)計(jì)執(zhí)行的時(shí)間小于當(dāng)前系統(tǒng)時(shí)間,那么就需要執(zhí)行榨惠,此時(shí)判定時(shí)間片是否為0奋早,如果為0,則調(diào)用removeMin方法將其移除赠橙,否則將task通過rescheduleMin設(shè)置最新時(shí)間并排序:
currentTime = System.currentTimeMillis();
executionTime = task.nextExecutionTime;
if (taskFired = (executionTime<=currentTime)) {
if (task.period == 0) { // Non-repeating, remove
queue.removeMin();
task.state = TimerTask.EXECUTED;
} else { // Repeating task, reschedule
queue.rescheduleMin(
task.period<0 ? currentTime - task.period
: executionTime + task.period);
}
}
這里可以看到耽装,period為負(fù)數(shù)的時(shí)候,就會被認(rèn)為是按照當(dāng)前系統(tǒng)時(shí)間+一個(gè)時(shí)間片來計(jì)算下一次時(shí)間简烤,就是前面說的schedule和scheduleAtFixedRate的區(qū)別了剂邮,其實(shí)內(nèi)部是通過正負(fù)數(shù)來判定的,也許java是不想增加參數(shù)横侦,而又想增加程序的可讀性挥萌,才這樣做。
同時(shí)你可以看到period為0枉侧,就是只執(zhí)行一次引瀑,所以時(shí)間片正負(fù)0都用上了,然后再看看mainLoop接下來的部分:
if (!taskFired)// Taskhasn't yet fired; wait
queue.wait(executionTime- currentTime);
如果任務(wù)執(zhí)行時(shí)間還未到榨馁,就等待一段時(shí)間憨栽,當(dāng)然這個(gè)等待很可能會被其他的線程操作add和cancel的時(shí)候被喚醒,因?yàn)閮?nèi)部有notify方法翼虫,所以這個(gè)時(shí)間并不是完全準(zhǔn)確屑柔。
最后:
if (taskFired) // Task fired; run it, holding no locks
task.run();
如果任務(wù)需要執(zhí)行,那么調(diào)用它的run方法珍剑,而并非啟動(dòng)一個(gè)新的線程或從線程池中獲取一個(gè)線程來執(zhí)行掸宛,所以TimerTask的run方法并不是多線程的run方法,雖然實(shí)現(xiàn)了Runnable招拙,但是僅僅是為了表示它是可執(zhí)行的唧瘾,并不代表它必須通過線程的方式來執(zhí)行的。
回過頭來再看看:
Timer和TimerTask的簡單組合是多線程的嘛别凤?不是饰序,一個(gè)Timer內(nèi)部包裝了“一個(gè)Thread”和“一個(gè)Task”隊(duì)列,這個(gè)隊(duì)列按照一定的方式將任務(wù)排隊(duì)處理规哪,包含的線程在Timer的構(gòu)造方法調(diào)用時(shí)被啟動(dòng)求豫,這個(gè)Thread的run方法無限循環(huán)這個(gè)Task隊(duì)列,若隊(duì)列為空且沒發(fā)生cancel操作,此時(shí)會一直等待注祖,如果等待完成后猾蒂,隊(duì)列還是為空均唉,則認(rèn)為發(fā)生了cancel從而跳出死循環(huán)是晨,結(jié)束任務(wù);循環(huán)中如果發(fā)現(xiàn)任務(wù)需要執(zhí)行的時(shí)間小于系統(tǒng)時(shí)間舔箭,則需要執(zhí)行罩缴,那么根據(jù)任務(wù)的時(shí)間片重新計(jì)算下次執(zhí)行時(shí)間,若時(shí)間片為0代表只執(zhí)行一次层扶,則直接從隊(duì)列移除即可箫章。
但是是否能實(shí)現(xiàn)多線程呢?可以镜会,任何東西是否是多線程完全看個(gè)人意愿檬寂,多個(gè)Timer自然就是多線程的,每個(gè)Timer都有自己的線程處理邏輯戳表,當(dāng)然Timer從這里來看并不是很適合很多任務(wù)在短時(shí)間內(nèi)的快速調(diào)度桶至,至少不是很適合同一個(gè)timer上掛很多任務(wù),在多線程的領(lǐng)域中我們更多是使用多線程中的:
Executors.newScheduledThreadPool
來完成對調(diào)度隊(duì)列中的線程池的處理匾旭,內(nèi)部通過new ScheduledThreadPoolExecutor來創(chuàng)建線程池的镣屹。