pool(三)——Timer
?1.關(guān)于Timer的三個(gè)維度
首先是 {@link java.util.Timer},這個(gè)是最外層的類,其中包含了{(lán)@link java.util.TaskQueue},這個(gè)是存放{@link java.util.TimerTask}的隊(duì)列——a priority queue of TimerTasks。
第二層是 {@link java.util.TimerThread},這個(gè)是{@link java.util.Timer}在初始化的時(shí)候創(chuàng)建并啟動(dòng)的一個(gè)線程,這個(gè)線程取任務(wù)并且執(zhí)行。
/*** Creates a new timer whose associated thread has the specified name.* The associated thread does <i>not</i>* {@linkplain Thread#setDaemon run as a daemon}.** @param name the name of the associated thread* @throws NullPointerException if {@code name} is null* @since 1.5*/public Timer(String name) {thread.setName(name);thread.start();}2.TimerThread
Thread的子類,在run方法中循環(huán)取任務(wù)。
public void run() {try {mainLoop();} finally {// Someone killed this Thread, behave as if Timer cancelledsynchronized(queue) {newTasksMayBeScheduled = false;queue.clear(); // Eliminate obsolete references}}} /*** The main timer loop. (See class comment.)*/private void mainLoop() {while (true) {try {TimerTask task;boolean taskFired;synchronized(queue) {// Wait for queue to become non-emptywhile (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 thinglong 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, removequeue.removeMin();task.state = TimerTask.EXECUTED;} else { // Repeating task, reschedulequeue.rescheduleMin(task.period<0 ? currentTime - task.period: executionTime + task.period);}}}if (!taskFired) // Task hasn't yet fired; waitqueue.wait(executionTime - currentTime);}if (taskFired) // Task fired; run it, holding no lockstask.run();} catch(InterruptedException e) {}}}3.task的創(chuàng)建——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);} /*** Schedule the specified timer task for execution at the specified* time with the specified period, in milliseconds. If period is* positive, the task is scheduled for repeated execution; if period is* zero, the task is scheduled for one-time execution. Time is specified* in Date.getTime() format. This method checks timer state, task state,* and initial execution time, but not period.** @throws IllegalArgumentException if <tt>time</tt> is negative.* @throws IllegalStateException if task was already scheduled or* cancelled, timer was cancelled, or timer thread terminated.* @throws NullPointerException if {@code task} is null*/private void sched(TimerTask task, long time, long period) {if (time < 0)throw new IllegalArgumentException("Illegal execution time.");// Constrain value of period sufficiently to prevent numeric// overflow while still being effectively infinitely large.if (Math.abs(period) > (Long.MAX_VALUE >> 1))period >>= 1;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();}}1.兩重鎖,先鎖隊(duì)列queue,再鎖task,task中有一個(gè)Object對(duì)象作為鎖
2.設(shè)置TimerTask的下次執(zhí)行時(shí)間
{@link java.util.TimerTask#nextExecutionTime} = System.currentTimeMillis()+delay
3.將任務(wù)添加到隊(duì)列中
{@link java.util.Timer#queue}
4.當(dāng)前任務(wù)如果是隊(duì)列的一個(gè)任務(wù),就執(zhí)行
task == {@link java.util.TaskQueue#getMin},調(diào)用queue的notify方法。如果不是,說明前面還有等待執(zhí)行的task,只入隊(duì)列,不用調(diào)用notify方法。
5.{@link java.util.TimerThread#mainLoop}
{@link java.util.TimerThread}會(huì)在{@link java.util.Timer}
構(gòu)造的時(shí)候啟動(dòng),進(jìn)而調(diào)用mainLoop方法。
最開始queue是空的,所以queue.await(),當(dāng)前線程掛起,當(dāng)Timer中添加TimerTask任務(wù)時(shí),
就會(huì)調(diào)用queue.notify()方法喚醒mainLoop線程。
?
4.根據(jù)優(yōu)先級(jí)對(duì)堆進(jìn)行重排序
1.TimerTask的入堆(queue)操作
delay時(shí)間設(shè)置的很長,就是為了讓任務(wù)不執(zhí)行,看看入隊(duì)列的比較操作,period這里完全用作標(biāo)志位,在debug的時(shí)候作為標(biāo)記區(qū)分不同的Task,看看排序狀況。
這里以3個(gè)為例,第一個(gè)入隊(duì)列,index是1,第二個(gè)入隊(duì)列,index是2,2>>1=1,然后拿queue[2]和queue[1]比較下次執(zhí)行時(shí)間,queue[2]比queue[1]早,所以交換順序。
第三個(gè)入隊(duì)列,index是3,3>>1=1,和第一個(gè)比,queue[3]比queue[1]要早,所以交換順序,所以現(xiàn)在queue[1]最早執(zhí)行,queue[2]和queue[3]的順序沒有考慮。每次入隊(duì)列的重排序操作在 {@link java.util.TaskQueue#fixUp} 方法中進(jìn)行
2.mainLoop執(zhí)行操作:
每次取queue的第一個(gè)task,如果該task還沒到執(zhí)行時(shí)間,就等待對(duì)應(yīng)的時(shí)間queue.wait(executionTime - currentTime)。
if (!taskFired) // Task hasn't yet fired; waitqueue.wait(executionTime - currentTime);如果這期間又來了一個(gè)優(yōu)先級(jí)更高(執(zhí)行順序更靠前)的task,入隊(duì)列時(shí)調(diào)用fixUp把當(dāng)前task排到隊(duì)列頭(優(yōu)先級(jí)更高),然后notify這個(gè)queue打斷這個(gè)wait,重新去取優(yōu)先級(jí)更高的task。
/*** Adds a new task to the priority queue.*/void add(TimerTask task) {// Grow backing store if necessaryif (size + 1 == queue.length)queue = Arrays.copyOf(queue, 2*queue.length);queue[++size] = task;fixUp(size);}如果到了執(zhí)行時(shí)間(wait結(jié)束),在下次循環(huán)的時(shí)候,就執(zhí)行該task。
如果是非重復(fù)任務(wù),調(diào)用removeMin移除當(dāng)前任務(wù),在removeMin中fixDown,進(jìn)行堆重排序。
如果是重復(fù)任務(wù)的話,還要調(diào)用rescheduleMin設(shè)置下次執(zhí)行的時(shí)間,在rescheduleMin中調(diào)用fixDown,進(jìn)行堆重排序。
3.{@link java.util.TaskQueue#fixDown}
/*** Establishes the heap invariant (described above) in the subtree* rooted at k, which is assumed to satisfy the heap invariant except* possibly for node k itself (which may have a nextExecutionTime greater* than its children's).** This method functions by "demoting" queue[k] down the hierarchy* (by swapping it with its smaller child) repeatedly until queue[k]'s* nextExecutionTime is less than or equal to those of its children.*/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 kidif (queue[k].nextExecutionTime <= queue[j].nextExecutionTime)break;TimerTask tmp = queue[j]; queue[j] = queue[k]; queue[k] = tmp;k = j;}}對(duì)剩下的Task進(jìn)行重新排序,把下次執(zhí)行時(shí)間最小的轉(zhuǎn)移到第一個(gè)任務(wù)的位置。
測試用例:
public static void test1() {timer.schedule(new TimerTask() {@Override public void run() {System.out.println("Time's up 1!---" + new Date().toString());// SleepUtil.sleep(30000);}}, 2000 * 1000, 11 * 1000);timer.schedule(new TimerTask() {@Override public void run() {System.out.println("Time's up 2!---" + new Date().toString());// SleepUtil.sleep(30000);}}, 1500 * 1000, 22 * 1000);timer.schedule(new TimerTask() {@Override public void run() {System.out.println("Time's up 3!---" + new Date().toString());// SleepUtil.sleep(30000);}}, 5 * 1000, 333333 * 1000);}5.TimerTask的執(zhí)行順序
可以得知Timer內(nèi)部是單線程執(zhí)行task的,一個(gè)timer對(duì)象只會(huì)啟用一個(gè)TimerThread的。
當(dāng)一個(gè)timer執(zhí)行多個(gè)任務(wù)時(shí),如果一個(gè)任務(wù)執(zhí)行的時(shí)間過長,后面任務(wù)執(zhí)行的時(shí)間可能就不是你預(yù)期執(zhí)行的時(shí)間了,因?yàn)橐粋€(gè)任務(wù)執(zhí)行完了才會(huì)執(zhí)行下個(gè)任務(wù)。
測試用例:
public static void test3() {timer.schedule(new TimerTask() {@Override public void run() {System.out.println("Time's up 1!---" + new Date().toString());SleepUtil.sleep(0);}}, 0, 2 * 1000);timer.schedule(new TimerTask() {@Override public void run() {System.out.println("Time's up 2!---" + new Date().toString());SleepUtil.sleep(5000);}}, 0, 2 * 1000);// EvictionTimer.schedule(evictor, delay, delay);}TimerTask執(zhí)行時(shí)間過長,超過了period,執(zhí)行5s,period是2s,這樣period相當(dāng)于失效了。因?yàn)橄麓螆?zhí)行的時(shí)間是這樣計(jì)算的,
{@link java.util.TimerTask#nextExecutionTime} = System.currentTimeMillis()+delay
所以,當(dāng)本次任務(wù)執(zhí)行結(jié)束(過了超過5s),到下次任務(wù)取出來判斷的執(zhí)行時(shí)間的時(shí)候,肯定已經(jīng)超過了原本應(yīng)該執(zhí)行的時(shí)間。
根據(jù)mainLoop中的邏輯,當(dāng)判斷執(zhí)行時(shí)間比當(dāng)前時(shí)間要早的話,直接執(zhí)行本次任務(wù)。
?
總結(jié)
以上是生活随笔為你收集整理的pool(三)——Timer的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: java闹钟程序_java 闹钟程序
- 下一篇: oppo android多大内存,OPP