zoukankan      html  css  js  c++  java
  • 【源代码】Timer和TimerTask源代码剖析

    Timer是java.util包中的一个工具类,提供了定时器的功能。

    我们能够构造一个Timer对象,然后调用其schedule方法在某个特定的时间或者若干延时之后去运行一个特定的任务。甚至你能够让其以特定频率一直运行某个任务,这个任务用TimerTask描写叙述,我们将须要的操作写在TimerTask类的run方法中就可以

    本着“知其然。知其所以然”的心态,我决定研究下这个类的源代码。

    打开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类的源代码分析过程。最后贴上一张图。帮助理解:


  • 相关阅读:
    UVa 658 (Dijkstra) It's not a Bug, it's a Feature!
    CodeForces Round #288 Div.2
    UVa 540 (团体队列) Team Queue
    UVa 442 (栈) Matrix Chain Multiplication
    CodeForces Round #287 Div.2
    CodeForces Round #286 Div.2
    CodeForces Round #285 Div.2
    UVa 12096 (STL) The SetStack Computer
    UVa 101 (模拟) The Blocks Problem
    UVa 12171 (离散化 floodfill) Sculpture
  • 原文地址:https://www.cnblogs.com/llguanli/p/7391455.html
Copyright © 2011-2022 走看看