首页 > 代码库 > 【源码】Timer和TimerTask源码剖析
【源码】Timer和TimerTask源码剖析
Timer是java.util包中的一个工具类,提供了定时器的功能。我们可以构造一个Timer对象,然后调用其schedule方法在某个特定的时间或者若干延时之后去执行一个特定的任务,甚至你可以让其以特定频率一直执行某个任务,这个任务用TimerTask描述,我们将需要的操作写在TimerTask类的run方法中即可。
注释写的很明白,TimerThread会在run方法中调用mainloop方法,这是一个死循环,不断从任务队列中取出任务,执行之,如果没有任务可执行,将会wait,等待队列非空,而Timer类的schedule方法会调用notify唤醒该线程,执行任务。
那么TimerThread何时被启动的呢?猜猜也能知道,肯定是Timer被创建时执行的:
当我们主线程执行完毕后,Timer线程可能仍然处于阻塞或者其他状态,有时这不是我们希望看到的,Timer类有这样一个构造器,可以让任务执行线程以守护线程的方式运行,这样当主线程执行完毕后,守护线程也会停止。
以上就是Timer类的源码分析过程,最后贴上一张图,帮助理解:
本着“知其然,知其所以然”的心态,我决定研究下这个类的源码。
打开Timer类的源码我发现了这样两个成员变量:
/** * The timer task queue. This data structure is shared with the timer * thread. The timer produces tasks, via its various schedule calls, * and the timer thread consumes, executing timer tasks as appropriate, * and removing them from the queue when they're obsolete. */ private final TaskQueue queue = new TaskQueue();//任务队列 /** * The timer thread. */ private final TimerThread thread = new TimerThread(queue);//执行线程
TaskQueue是一个优先级队列,存放了我们将要执行的TimerTask对象,TimerTask对象是通过Timer类的一系列schedule方法加入队列的,TimerThread负责不断取出TaskQueue中的任务,然后执行之,也就是说,所有的任务都是是在子线程中执行的。TaskQueue队列是以其下次执行时间的先后排序的,TimerThread每次取出的都是需要最先执行的TimerTask。(跟android中的Handler机制很类似~)
优先级队列跟普通队列的最大区别就是,优先级队列每次出队的都是优先级最高的元素,并不是按先进先出的方式。这里优先级队列的实现使用的是堆结构(当然,你也可以使用普通链表,但是每次出队得花O(n)的时间遍历链表找到优先级最大的元素,不划算),插入及更新操作都能维持在O(logn):
class TaskQueue { /** * Priority queue represented as a balanced binary heap: the two children * of queue[n] are queue[2*n] and queue[2*n+1]. The priority queue is * ordered on the nextExecutionTime field: The TimerTask with the lowest * nextExecutionTime is in queue[1] (assuming the queue is nonempty). For * each node n in the heap, and each descendant of n, d, * n.nextExecutionTime <= d.nextExecutionTime. */ private TimerTask[] queue = new TimerTask[128];//使用数组存储堆元素,最大值128 /** * The number of tasks in the priority queue. (The tasks are stored in * queue[1] up to queue[size]). */ private int size = 0;//任务数 /** * Returns the number of tasks currently on the queue. */ int size() { return size; } /** * Adds a new task to the priority queue. */ void add(TimerTask task) {//将TimerTask任务添加到此队列中 // Grow backing store if necessary if (size + 1 == queue.length) queue = Arrays.copyOf(queue, 2*queue.length); queue[++size] = task; fixUp(size);//调整堆结构---->所谓的上滤 } /** * Return the "head task" of the priority queue. (The head task is an * task with the lowest nextExecutionTime.) */ TimerTask getMin() {//优先级最高的元素始终在第一个位置 return queue[1]; } /** * Return the ith task in the priority queue, where i ranges from 1 (the * head task, which is returned by getMin) to the number of tasks on the * queue, inclusive. */ TimerTask get(int i) { return queue[i]; } /** * Remove the head task from the priority queue. */ void removeMin() { queue[1] = queue[size]; queue[size--] = null; // Drop extra reference to prevent memory leak fixDown(1);//调整堆结构----->所谓的下滤 } /** * Removes the ith element from queue without regard for maintaining * the heap invariant. Recall that queue is one-based, so * 1 <= i <= size. */ void quickRemove(int i) { assert i <= size; queue[i] = queue[size]; queue[size--] = null; // Drop extra ref to prevent memory leak } /** * Sets the nextExecutionTime associated with the head task to the * specified value, and adjusts priority queue accordingly. */ void rescheduleMin(long newTime) { queue[1].nextExecutionTime = newTime; fixDown(1); } /** * Returns true if the priority queue contains no elements. */ boolean isEmpty() { return size==0; } /** * Removes all elements from the priority queue. */ void clear() { // Null out task references to prevent memory leak for (int i=1; i<=size; i++) queue[i] = null; size = 0; } /** * Establishes the heap invariant (described above) assuming the heap * satisfies the invariant except possibly for the leaf-node indexed by k * (which may have a nextExecutionTime less than its parent's). * * This method functions by "promoting" queue[k] up the hierarchy * (by swapping it with its parent) repeatedly until queue[k]'s * nextExecutionTime is greater than or equal to that of its parent. */ private void fixUp(int k) { while (k > 1) { int j = k >> 1; if (queue[j].nextExecutionTime <= queue[k].nextExecutionTime) break; TimerTask tmp = queue[j]; queue[j] = queue[k]; queue[k] = tmp; k = j; } } /** * 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 kid if (queue[k].nextExecutionTime <= queue[j].nextExecutionTime) break; TimerTask tmp = queue[j]; queue[j] = queue[k]; queue[k] = tmp; k = j; } } /** * Establishes the heap invariant (described above) in the entire tree, * assuming nothing about the order of the elements prior to the call. */ void heapify() {//建堆操作,从第一个非叶子结点开始。 for (int i = size/2; i >= 1; i--) fixDown(i); } }
TaskQueue内部是一个TimerTask数组,数组元素从1开始,这个数组就是所谓的堆,还是个小顶堆,堆顶元素
始终为第一个元素,每次添加TimerTask都会调用fixup上滤操作,维持堆的特性,每次删除堆顶元素后需要调用fixdown下滤操作,维持堆的特性。heapify是一个建堆函数(类似堆排序中的建堆操作),从第一个非叶子结点开始。
了解TaskQueue后,再看TimerThread类:
class TimerThread extends Thread { /** * This flag is set to false by the reaper to inform us that there * are no more live references to our Timer object. Once this flag * is true and there are no more tasks in our queue, there is no * work left for us to do, so we terminate gracefully. Note that * this field is protected by queue's monitor! */ boolean newTasksMayBeScheduled = true; /** * Our Timer's queue. We store this reference in preference to * a reference to the Timer so the reference graph remains acyclic. * Otherwise, the Timer would never be garbage-collected and this * thread would never go away. */ private TaskQueue queue;//持有任务队列的引用 TimerThread(TaskQueue queue) { this.queue = queue; } public void run() { try { mainLoop();//执行一个死循环,不断从队列中取出任务并执行,没有任务时会阻塞 } finally { // Someone killed this Thread, behave as if Timer cancelled synchronized(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-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) { } } } }
注释写的很明白,TimerThread会在run方法中调用mainloop方法,这是一个死循环,不断从任务队列中取出任务,执行之,如果没有任务可执行,将会wait,等待队列非空,而Timer类的schedule方法会调用notify唤醒该线程,执行任务。
private void sched(TimerTask task, long time, long period) {//所有的schedule方法都会调用此方法 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();//唤醒任务执行线程 } }
那么TimerThread何时被启动的呢?猜猜也能知道,肯定是Timer被创建时执行的:
public Timer(String name) { thread.setName(name); thread.start();//启动线程 }
当我们主线程执行完毕后,Timer线程可能仍然处于阻塞或者其他状态,有时这不是我们希望看到的,Timer类有这样一个构造器,可以让任务执行线程以守护线程的方式运行,这样当主线程执行完毕后,守护线程也会停止。
public Timer(boolean isDaemon) { this("Timer-" + serialNumber(), isDaemon); }
以上就是Timer类的源码分析过程,最后贴上一张图,帮助理解:
【源码】Timer和TimerTask源码剖析
声明:以上内容来自用户投稿及互联网公开渠道收集整理发布,本网站不拥有所有权,未作人工编辑处理,也不承担相关法律责任,若内容有误或涉及侵权可进行投诉: 投诉/举报 工作人员会在5个工作日内联系你,一经查实,本站将立刻删除涉嫌侵权内容。