pool(三)——Timer
?1.關于Timer的三個維度
首先是 {@link java.util.Timer},這個是最外層的類,其中包含了{@link java.util.TaskQueue},這個是存放{@link java.util.TimerTask}的隊列——a priority queue of TimerTasks。
第二層是 {@link java.util.TimerThread},這個是{@link java.util.Timer}在初始化的時候創建并啟動的一個線程,這個線程取任務并且執行。
/*** 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方法中循環取任務。
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的創建——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.兩重鎖,先鎖隊列queue,再鎖task,task中有一個Object對象作為鎖
2.設置TimerTask的下次執行時間
{@link java.util.TimerTask#nextExecutionTime} = System.currentTimeMillis()+delay
3.將任務添加到隊列中
{@link java.util.Timer#queue}
4.當前任務如果是隊列的一個任務,就執行
task == {@link java.util.TaskQueue#getMin},調用queue的notify方法。如果不是,說明前面還有等待執行的task,只入隊列,不用調用notify方法。
5.{@link java.util.TimerThread#mainLoop}
{@link java.util.TimerThread}會在{@link java.util.Timer}
構造的時候啟動,進而調用mainLoop方法。
最開始queue是空的,所以queue.await(),當前線程掛起,當Timer中添加TimerTask任務時,
就會調用queue.notify()方法喚醒mainLoop線程。
?
4.根據優先級對堆進行重排序
1.TimerTask的入堆(queue)操作
delay時間設置的很長,就是為了讓任務不執行,看看入隊列的比較操作,period這里完全用作標志位,在debug的時候作為標記區分不同的Task,看看排序狀況。
這里以3個為例,第一個入隊列,index是1,第二個入隊列,index是2,2>>1=1,然后拿queue[2]和queue[1]比較下次執行時間,queue[2]比queue[1]早,所以交換順序。
第三個入隊列,index是3,3>>1=1,和第一個比,queue[3]比queue[1]要早,所以交換順序,所以現在queue[1]最早執行,queue[2]和queue[3]的順序沒有考慮。每次入隊列的重排序操作在 {@link java.util.TaskQueue#fixUp} 方法中進行
2.mainLoop執行操作:
每次取queue的第一個task,如果該task還沒到執行時間,就等待對應的時間queue.wait(executionTime - currentTime)。
if (!taskFired) // Task hasn't yet fired; waitqueue.wait(executionTime - currentTime);如果這期間又來了一個優先級更高(執行順序更靠前)的task,入隊列時調用fixUp把當前task排到隊列頭(優先級更高),然后notify這個queue打斷這個wait,重新去取優先級更高的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);}如果到了執行時間(wait結束),在下次循環的時候,就執行該task。
如果是非重復任務,調用removeMin移除當前任務,在removeMin中fixDown,進行堆重排序。
如果是重復任務的話,還要調用rescheduleMin設置下次執行的時間,在rescheduleMin中調用fixDown,進行堆重排序。
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;}}對剩下的Task進行重新排序,把下次執行時間最小的轉移到第一個任務的位置。
測試用例:
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的執行順序
可以得知Timer內部是單線程執行task的,一個timer對象只會啟用一個TimerThread的。
當一個timer執行多個任務時,如果一個任務執行的時間過長,后面任務執行的時間可能就不是你預期執行的時間了,因為一個任務執行完了才會執行下個任務。
測試用例:
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執行時間過長,超過了period,執行5s,period是2s,這樣period相當于失效了。因為下次執行的時間是這樣計算的,
{@link java.util.TimerTask#nextExecutionTime} = System.currentTimeMillis()+delay
所以,當本次任務執行結束(過了超過5s),到下次任務取出來判斷的執行時間的時候,肯定已經超過了原本應該執行的時間。
根據mainLoop中的邏輯,當判斷執行時間比當前時間要早的話,直接執行本次任務。
?
總結
以上是生活随笔為你收集整理的pool(三)——Timer的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: java闹钟程序_java 闹钟程序
- 下一篇: oppo android多大内存,OPP