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) {
                }
            }
        }
    

      

  • 相关阅读:
    启动Mysql后找不到服务或出现找不到指定文件
    WEB-MVC模式图示
    Java中Map集合的遍历方式
    sun.misc.BASE64Encoder找不到jar包的解决方法
    Tomcat常用的网站发布方式
    Sql Server查询行号
    Mysql下载安装问题
    【数学】环逆序
    【搜索】【the first editoral】OpenJudge 我是最快的马
    The First Blog
  • 原文地址:https://www.cnblogs.com/chenjunjie12321/p/8671303.html
Copyright © 2011-2022 走看看