日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁(yè) > 编程资源 > 编程问答 >内容正文

编程问答

【定时任务】JDK java.util.Timer定时器的实现原理

發(fā)布時(shí)間:2024/4/14 编程问答 42 豆豆
生活随笔 收集整理的這篇文章主要介紹了 【定时任务】JDK java.util.Timer定时器的实现原理 小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

在程序中簡(jiǎn)單實(shí)用Timer的方法,參考學(xué)習(xí)。
定時(shí)任務(wù),也叫定時(shí)器,是指在指定的時(shí)間執(zhí)行指定任務(wù)的機(jī)制,類似于Windows自帶的計(jì)劃任務(wù)。JDK中提供了定時(shí)器的支持—java.util.Timer,下面我們來系統(tǒng)學(xué)習(xí)一下它的實(shí)現(xiàn)原理。

Timer主要由三個(gè)部分組成: 任務(wù)TimerTask、任務(wù)隊(duì)列TaskQueue和 任務(wù)調(diào)試者TimerThread。多個(gè)任務(wù)單元 TimerTask按照一定的優(yōu)先級(jí)組成了任務(wù)隊(duì)列TaskQueue,任務(wù)調(diào)度TimerThread按照一定的規(guī)則每次取出任務(wù)隊(duì)列中的一個(gè)任務(wù)進(jìn)行處理

TimerTask 任務(wù)單元

//*********任務(wù)狀態(tài)*****************//VIRGIN表示Task剛剛被創(chuàng)建static final int VIRGIN = 0;//************幾個(gè)狀態(tài)常量******************//SCHEDULED表示Task已經(jīng)被加入TaskQueue中,等待調(diào)度static final int SCHEDULED = 1;//EXECUTED表示Task已經(jīng)被執(zhí)行static final int EXECUTED = 2;//CANCELLED表示Task已經(jīng)被取消//**************兩個(gè)重要的成員變量******************//nextExecutionTime這個(gè)成員變量用到記錄該任務(wù)下次執(zhí)行時(shí)間, 其格式和System.currentTimeMillis()一致.這個(gè)值是作為任務(wù)隊(duì)列中任務(wù)排序的依據(jù). 任務(wù)調(diào)試者執(zhí)行每個(gè)任務(wù)前會(huì)對(duì)這個(gè)值作處理,重新計(jì)算下一次任務(wù)執(zhí)行時(shí)間,并為這個(gè)變量賦值.long nextExecutionTime; //period 用來描述任務(wù)的執(zhí)行方式: 0表示不重復(fù)執(zhí)行的任務(wù). 正數(shù)表示固定速率執(zhí)行的任務(wù). 負(fù)數(shù)表示固定延遲執(zhí)行的任務(wù). (固定速率: 不考慮該任務(wù)上一次執(zhí)行情況,始終從開始時(shí)間算起的每period執(zhí)行下一次。固定延遲: 考慮該任務(wù)一次執(zhí)行情況,在上一次執(zhí)行后period執(zhí)行下一次)。long period = 0;

TaskQueue任務(wù)隊(duì)列

TaskQueue是用來保存TimerTask的隊(duì)列,是一個(gè)數(shù)組, 采用平衡二叉堆來實(shí)現(xiàn)優(yōu)先級(jí)調(diào)度, 并且是一個(gè)最小堆, 這個(gè)堆中queue[n] 的孩子是queue[2*n] 和 queue[2*n+1]。任務(wù)隊(duì)列的優(yōu)先級(jí)按照TimerTask類的成員變量nextExecutionTime值來排序。在任務(wù)隊(duì)列中, nextExecutionTime最小就是所有任務(wù)中最早要被調(diào)度來執(zhí)行的(也就是堆頂?shù)腡ask), 所以被安排在queue[1] (假設(shè)任務(wù)隊(duì)列非空),對(duì)于堆中任意一個(gè)節(jié)點(diǎn)m,和他的任意子孫節(jié)點(diǎn)n,一定遵循: m.nextExecutionTime <= n.nextExecutionTime.

下面是TaskQueue的核心代碼,也是最小堆的實(shí)現(xiàn)代碼:

添加任務(wù):

/***首先會(huì)判斷是否已經(jīng)滿了,如果已經(jīng)滿了, 那么容量擴(kuò)大至原來2倍, 然后將需要添加的任務(wù)放到隊(duì)列最后. 之后就會(huì)調(diào)用fixUp 方法來進(jìn)行隊(duì)列中任務(wù)優(yōu)先級(jí)調(diào)整.*/ void add(TimerTask task) { if (size + 1 == queue.length) queue = Arrays.copyOf(queue, 2 * queue.length); queue[++size] = task; fixUp(size); } /*** fixUp方法的作用是盡量將隊(duì)列中指定位置(k)的任務(wù)向隊(duì)列前面移動(dòng), 即提高它的優(yōu)先級(jí). 因?yàn)樾录尤氲姆椒ê苡锌赡鼙纫呀?jīng)在任務(wù)隊(duì)列中的其它任務(wù)要更早執(zhí)行.*/ private void fixUp(int k) { while (k > 1) { int j = k >> 1; // 對(duì)于正數(shù),右移位 <==> j = k/2, 所以j的位置就是k的父親節(jié)點(diǎn) if (queue[j].nextExecutionTime <= queue[k].nextExecutionTime) break; TimerTask tmp = queue[j]; queue[j] = queue[k]; queue[k] = tmp; k = j; } }

總結(jié):這個(gè)過程可以這個(gè)描述: 不斷地將k位置上元素和它的父親進(jìn)行比較, 如果發(fā)現(xiàn)孩子節(jié)點(diǎn)比父親小的時(shí)候, 那么將父親和孩子位置互換. 直到最小的來到隊(duì)列第一個(gè)位置。

移除任務(wù):

/*** 首先直接將當(dāng)前任務(wù)隊(duì)列中最后一個(gè)任務(wù)賦給queue[1], 然后將隊(duì)列中任務(wù)數(shù)量--, 最后和上面類似, 但是這里是調(diào)用fixDown(int k)方法了, 盡量將k位置的任務(wù)向隊(duì)列后面移動(dòng).*/ void removeMin() { queue[1] = queue[size]; queue[size--] = null; // Drop extra reference to prevent memory leak fixDown(1); } /** * 將k位置的元素向堆底方向移動(dòng)* 1. j = k << 1, .<br> * 2. 將 j 精確定位到較小的兒子.<br> * 3. 然后k與j比較,如果k大于j的話, 那么互換< * 4.繼續(xù)... */ private void fixDown(int k) { int j; // 如果還沒有到隊(duì)列的最后,并且沒有溢出( j > 0 ),在沒有出現(xiàn)溢出的情況下, j = k << 1 等價(jià)于 j = 2 * k,將j定位到兒子中 while ((j = k << 1) <= size && j > 0) { // 找到k的兩個(gè)孩子中小的那個(gè). if (j < size && queue[j].nextExecutionTime > queue[j + 1].nextExecutionTime) j++; // 找到這個(gè)較小的孩子后,(此時(shí)k是父親,j是較小的兒子),父親和兒子互換位置,即k和j換位子.這樣一直下去就可以將這個(gè)較大的queue[1]向下堆底移動(dòng)了. if (queue[k].nextExecutionTime <= queue[j].nextExecutionTime) break; TimerTask tmp = queue[j]; queue[j] = queue[k]; queue[k] = tmp; k = j; } }

TimerThread任務(wù)調(diào)度

TimerThread就是用來調(diào)度TaskQueue中的任務(wù)的線程。關(guān)于任務(wù)調(diào)度主要有一個(gè)成員變量 newTasksMayBeScheduled和調(diào)度方法mainLoop()。

boolean newTasksMayBeScheduled = true; private void mainLoop() { while (true) { try { TimerTask task; boolean taskFired = false; synchronized (queue) { while (queue.isEmpty() && newTasksMayBeScheduled) { queue.wait(); } if (queue.isEmpty()) break; // 直接挑出mainLoop了. long currentTime, executionTime; task = queue.getMin(); // 獲取這個(gè)任務(wù)隊(duì)列第一個(gè)任務(wù) 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) { // period表示不重復(fù),移除出隊(duì)列 queue.removeMin(); task.state = TimerTask.EXECUTED; } else { //重復(fù)執(zhí)行的設(shè)置重新調(diào)度 queue.rescheduleMin(task.period < 0 ? currentTime - task.period : executionTime + task.period); } } }//釋放鎖 if (!taskFired) queue.wait(executionTime - currentTime); } if (taskFired) // Task fired; run it, holding no locks task.run(); } catch (InterruptedException e) { } }// while(true) }

newTasksMayBeScheduled變量用來表示是否需要繼續(xù)等待新任務(wù)了。默認(rèn)情況這個(gè)變量是true , 并且這個(gè)變量一直是true的。只有兩種情況的時(shí)候會(huì)變成 false :
1.當(dāng)調(diào)用Timer的cancel方法
2.沒有引用指向Timer對(duì)象了.

任務(wù)調(diào)度mainLoop()方法中的一個(gè)while可以理解為一次任務(wù)調(diào)度

STEP 1 : 判斷任務(wù)隊(duì)列中是否還有任務(wù), 如果任務(wù)隊(duì)列為空了, 但是newTasksMayBeScheduled變量還是true, 表明 需要繼續(xù)等待新任務(wù), 所以一直等待。

STEP 2 : 等待喚醒后, 再次判斷隊(duì)列中是否有任務(wù). 如果還是沒有任務(wù),那么直接結(jié)束定時(shí)器工作了.因?yàn)閝ueue只在兩個(gè)地方被調(diào)用: addTask和cancel 1.向任務(wù)隊(duì)列中增加任務(wù)會(huì)喚醒 2.timer.cancel()的時(shí)候也會(huì)喚醒. 那么這里如果還是empty,那么就是cancel的喚醒了,所以可以結(jié)束timer工作了。

STEP 3 : 從任務(wù)隊(duì)列中取出第一個(gè)任務(wù),即nextExecutionTime最小的那個(gè)任務(wù)。

STEP 4: 判斷這個(gè)任務(wù)是否已經(jīng)被取消. 如果已經(jīng)被取消了,那么就直接從任務(wù)隊(duì)列中移除這個(gè)任務(wù)(removeMin() ),然后直接進(jìn)入下一個(gè)任務(wù)調(diào)度周期。

STEP 5 : 判斷是否到了或者已經(jīng)超過了這個(gè)任務(wù)應(yīng)該執(zhí)行的時(shí)間了。如果到了 , 不會(huì)立即執(zhí)行它,而是會(huì)在這次循環(huán)的最后來執(zhí)行它。
這里做的事情可以看作是為下一個(gè)調(diào)度周期進(jìn)行準(zhǔn)備:包括:

1. 判斷是否是重復(fù)(repeating)任務(wù),如果 task.period == 0, 那么就不是重復(fù)任務(wù),所以可以直接將這個(gè)任務(wù)從任務(wù)隊(duì)列中移除了(removeMin() ),因?yàn)闆]有必要留到下一個(gè)調(diào)度周期中去了.2. 如果是需要重復(fù)執(zhí)行的任務(wù), 那么就要重新設(shè)置這個(gè)任務(wù)的nextExecutionTime,即調(diào)用方法queue.rescheduleMin(long) ,這個(gè)方法中會(huì)調(diào)用fixDown(1) 負(fù)責(zé)重新調(diào)整任務(wù)隊(duì)列的優(yōu)先級(jí)順序.

如果還沒有到執(zhí)行時(shí)間 , 一直等到 queue.wait(executionTime - currentTime),并且等待完畢后,似乎可以開始運(yùn)行了, 但是這里設(shè)計(jì)成不立即運(yùn)行,而是直接進(jìn)入下一個(gè)任務(wù)調(diào)度周期.(因?yàn)閠askFired =false,所以不會(huì)在這次進(jìn)行執(zhí)行的.)

STEP6: 開始調(diào)用任務(wù)的run方法運(yùn)行任務(wù)。

還有一點(diǎn)需要注意:
在step2中我們學(xué)習(xí)到,TimerThread的調(diào)度核心是起一個(gè)while循環(huán),不斷檢查是否有task需要執(zhí)行,其中兩次調(diào)用了queue.wait()方法。1.向任務(wù)隊(duì)列中增加任務(wù)會(huì)喚醒 2.timer.cancel()的時(shí)候也會(huì)喚醒,兩種情況下queue.notify()方法會(huì)被調(diào)用。

但是是否上面兩種情況調(diào)用notify就已經(jīng)足夠了?當(dāng)queue為空,并且沒人調(diào)用add或cancel方法時(shí),TimerThread永遠(yuǎn)都不會(huì)stop。不用擔(dān)心,對(duì)于這個(gè)地方的處理JDK加上了一種比較保險(xiǎn)的方法:

/*** This object causes the timer's task execution thread to exit* gracefully when there are no live references to the Timer object and no* tasks in the timer queue. It is used in preference to a finalizer on* Timer as such a finalizer would be susceptible to a subclass's* finalizer forgetting to call it.*/ private final Object threadReaper = new Object() {protected void finalize() throws Throwable {synchronized(queue) {thread.newTasksMayBeScheduled = false;queue.notify(); // In case queue is empty.}} };

用到了Object對(duì)象的finalize方法,大家都知道finalize方法是對(duì)象被GC的時(shí)候調(diào)用的。上述做法的思路是:當(dāng)一個(gè)Timer已經(jīng)沒有任何對(duì)象引用時(shí),自然不會(huì)有新的Task加入到隊(duì)列中,Timer對(duì)象自然也就會(huì)被垃圾回收,此時(shí)TimerThread也就應(yīng)該stop了,所以在垃圾回收的時(shí)候還應(yīng)該把newTasksMayBeScheduled設(shè)置為false,并且喚起正在wait的TimerThread線程。所以說,如果你創(chuàng)建的Timer不再需要了,最好是調(diào)用cancel接口手動(dòng)取消,否則的話TimerThread就需要等到垃圾回收的時(shí)候才會(huì)stop。

總結(jié)

以上是生活随笔為你收集整理的【定时任务】JDK java.util.Timer定时器的实现原理的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。