zoukankan      html  css  js  c++  java
  • schedule与scheduleAtFixedRate之Timer源码分析

    执行Timer任务调度方法有如下几种:

    这些方法最后调用的都是这个方法:

     private void sched(TimerTask task, long time, long period) 
    
     
    这个方法的作用是将task放入Timer实例的共享变量queue(TaskQueue类型)中。源码如下:
    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();
            }
        }
    
     
    TaskQueue按照一个“完全二叉树堆”来进行排序,完全二叉树堆的中根节点的值最大(或者最小),每节点的值大于(或者小于)其两个子节点。
     
    queue.add(task)时首先将该任务放到队列最后,会发生“上浮”动作,最终保持完全二叉树堆的根节点存放task.nextExecutionTime最小的那个task,如果要执行任务直接从TaskQueue的根节点获取那个值执行,执行完任务后从队列中移除。
     
    这篇文章以图文的方式对二叉树堆算法讲解的很清晰 http://weixin.niurenqushi.com/article/2016-06-17/4326390.html,可以参考一下。
     
    回到源码,sched(TimerTask task, long time, long period) 方法会将一个任务加入到TaskQueue队列后,如果这个任务在队列中的根节点(也就是nextExecutionTime最小的那个task),那就发出notify通知,激活其他占用了该任务由于wait()而阻塞状态的线程。
     
    那这个线程主要涉及那一块呢?答案是Timer的线程体,源码如下:
    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,等待被notify激活
                        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) { // 如果period=0,则任务进行一次便结束,并移出队列
                                    queue.removeMin();
                                    task.state = TimerTask.EXECUTED;
                                } else { // 设置下一次执行的时间
                                         // task.period<0 对应 scheduleAtFixedRate 方法
                                         // task.period>0 对应 schedule 方法
                                    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) {
                }
            }
        }
    

      

  • 相关阅读:
    Learn Prolog Now 翻译
    Learn Prolog Now 翻译
    Learn Prolog Now 翻译
    Learn Prolog Now 翻译
    Learn Prolog Now 翻译
    Learn Prolog Now 翻译
    Learn Prolog Now 翻译
    Learn Prolog Now 翻译
    Learn Prolog Now 翻译
    Learn Prolog Now 翻译
  • 原文地址:https://www.cnblogs.com/chenjunjie12321/p/8671303.html
Copyright © 2011-2022 走看看