Java.util.Timer深入JDK代码及工作原理

来源:互联网 发布:linux怎么运行sh脚本 编辑:程序博客网 时间:2024/04/19 10:58

 Timer是JDK中的定时调度类,主要用来定时触发任务:

一:首先我们来看一下TimerTask的java代码,这样方便我们理解它的一些方法和作用:

这个类是个抽象类比较简单,有四个常量表示定时器任务的状态,还有一个Object类型lock对象,相当一把锁,控制线程对定时器任务状态的同步访问。

nextExecutionTime 这个成员变量用到记录该任务下次执行时间, 其格式和System.currentTimeMillis()一致. 

这个值是作为任务队列中任务排序的依据. 任务调试者执行每个任务前会对这个值作处理,重新计算下一次任务执行时间,并为这个变量赋值. 
  period 用来描述任务的执行方式: 0表示不重复执行的任务. 正数表示固定速率执行的任务. 负数表示固定延迟执行的任务. 
(固定速率: 不考虑该任务上一次执行情况,始终从开始时间算起的每period执行下一次.   固定延迟: 考虑该任务一次执行情况,在上一次执行后period执行下一次). 

  1. public abstract class TimerTask implements Runnable {  
  2.     //这个对象是用来控制访问TimerTask内部构件。锁机制  
  3.     final Object lock = new Object();  
  4.     //定时器任务的状态  
  5.     int state = VIRGIN;  
  6.     //定时器任务默认的状态,表示还没有被安排  
  7.     static final int VIRGIN = 0;  
  8.     //表示定时器任务被安排了  
  9.     static final int SCHEDULED   = 1;  
  10.     //表示定时器任务执行  
  11.     static final int EXECUTED    = 2;  
  12.     //表示定时器任务取消  
  13.     static final int CANCELLED   = 3;  
  14.     //下次执行任务时间  
  15.     long nextExecutionTime;  
  16.     long period = 0;  
  17.   
  18.     protected TimerTask() {  
  19.     }  
  20.    // 此计时器任务要执行的操作。  
  21.     public abstract void run();  
  22.   
  23.     // 取消此计时器任务。  
  24.     public boolean cancel() {  
  25.         synchronized(lock) {  
  26.             boolean result = (state == SCHEDULED);  
  27.             state = CANCELLED;  
  28.             return result;  
  29.         }  
  30.     }  
  31.     // 返回此任务最近实际 执行的已安排 执行时间。  
  32.     public long scheduledExecutionTime() {  
  33.         synchronized(lock) {  
  34.             return (period < 0 ? nextExecutionTime + period  
  35.                                : nextExecutionTime - period);  
  36.         }  
  37.     }  
  38. }

二、深入JDK源代码-Timer类,Timer中最主要由三个部分组成: 任务 TimerTask 、  任务队列: TaskQueue queue 和 任务调试者:TimerThread thread 

1.任务队列 TaskQueue,它是Timer的一个内部类。 
    事实上任务队列是一个数组, 采用平衡二叉堆来实现他的优先级调度, 并且是一个小顶堆. 需要注意的是, 这个堆中queue[n] 的孩子是queue[2*n] 和 queue[2*n+1]. 
    任务队列的优先级按照TimerTask类的成员变量nextExecutionTime值来排序(注意, 这里的任务指的是那些交由定时器来执行的, 继承TimerTask的对象). 
    在任务队列中, nextExecutionTime最小就是所有任务中最早要被调度来执行的, 所以被安排在queue[1] (假设任务队列非空). 
    对于堆中任意一个节点n, 和他的任意子孙节点d,一定遵循: n.nextExecutionTime <= d.nextExecutionTime. 

  1. // 任务队列  
  2. class TaskQueue {  
  3.     // 计时器任务数组,默认大小为128  
  4.     private TimerTask[] queue = new TimerTask[128];  
  5.   
  6.     private int size = 0;  
  7.   
  8.     int size() {  
  9.         return size;  
  10.     }  
  11.   
  12.     // 加入队列  
  13.     void add(TimerTask task) {  
  14.         // Grow backing store if necessary  
  15.         if (size + 1 == queue.length)  
  16.             // 队列以两倍的速度扩容  
  17.             queue = Arrays.copyOf(queue, 2 * queue.length);  
  18.   
  19.         queue[++size] = task;  
  20.         fixUp(size);  
  21.     }  
  22.   
  23.     // 获取队列的地二个元素,即第一个任务,第一个元素存储的是  
  24.     TimerTask getMin() {  
  25.         return queue[1];  
  26.     }  
  27.   
  28.     TimerTask get(int i) {  
  29.         return queue[i];  
  30.     }  
  31.   
  32.     // 消除头任务从优先队列。  
  33.     void removeMin() {  
  34.         queue[1] = queue[size];  
  35.         queue[size--] = null// Drop extra reference to prevent memory leak  
  36.         fixDown(1);  
  37.     }  
  38.   
  39.     /** 
  40.      * Removes the ith element from queue without regard for maintaining the 
  41.      * heap invariant. Recall that queue is one-based, so 1 <= i <= size. 
  42.      */  
  43.     void quickRemove(int i) {  
  44.         // 断言,在这里只起测试作用  
  45.         assert i <= size;  
  46.   
  47.         queue[i] = queue[size];  
  48.         queue[size--] = null// Drop extra ref to prevent memory leak  
  49.     }  
  50.   
  51.     /** 
  52.      * Sets the nextExecutionTime associated with the head task to the specified 
  53.      * value, and adjusts priority queue accordingly. 
  54.      */  
  55.     void rescheduleMin(long newTime) {  
  56.         queue[1].nextExecutionTime = newTime;  
  57.         fixDown(1);  
  58.     }  
  59.   
  60.     /** 
  61.      * Returns true if the priority queue contains no elements. 
  62.      */  
  63.     boolean isEmpty() {  
  64.         return size == 0;  
  65.     }  
  66.   
  67.     /** 
  68.      * Removes all elements from the priority queue. 
  69.      */  
  70.     void clear() {  
  71.         // Null out task references to prevent memory leak  
  72.         for (int i = 1; i <= size; i++)  
  73.             queue[i] = null;  
  74.   
  75.         size = 0;  
  76.     }  
  77.   
  78.     // 进行队列中任务优先级调整. fixUp方法的作用是尽量将队列中指定位置(k)的任务向队列前面移动,  
  79.     // 即提高它的优先级. 因为新加入的方法很有可能比已经在任务队列中的其它任务要更早执行.  
  80.     private void fixUp(int k) {  
  81.         while (k > 1) {  
  82.             int j = k >> 1;// 左移一位,相当于除以2  
  83.             if (queue[j].nextExecutionTime <= queue[k].nextExecutionTime)  
  84.                 break;  
  85.             TimerTask tmp = queue[j];  
  86.             queue[j] = queue[k];  
  87.             queue[k] = tmp;  
  88.             k = j;  
  89.         }  
  90.     }  
  91.   
  92.     // 从任务队列中移除一个任务的过程, 首先直接将当前任务队列中最后一个任务赋给queue[1],  
  93.     // 然后将队列中任务数量--, 最后和上面类似, 但是这里是调用fixDown(int k)方法了, 尽量将k位置的任务向队列后面移动.  
  94.     private void fixDown(int k) {  
  95.         int j;  
  96.         while ((j = k << 1) <= size && j > 0) {  
  97.             if (j < size  
  98.                     && queue[j].nextExecutionTime > queue[j + 1].nextExecutionTime)  
  99.                 j++; // j indexes smallest kid  
  100.             if (queue[k].nextExecutionTime <= queue[j].nextExecutionTime)  
  101.                 break;  
  102.             TimerTask tmp = queue[j];  
  103.             queue[j] = queue[k];  
  104.             queue[k] = tmp;  
  105.             k = j;  
  106.         }  
  107.     }  
  108.   
  109.     /** 
  110.      * Establishes the heap invariant (described above) in the entire tree, 
  111.      * assuming nothing about the order of the elements prior to the call. 
  112.      */  
  113.     void heapify() {  
  114.         for (int i = size / 2; i >= 1; i--)  
  115.             fixDown(i);  
  116.     }  
  117. }

2.任务调度 TimerThread 

Java代码  收藏代码
  1. // 计时器线程  
  2. class TimerThread extends Thread {  
  3.   
  4.     // 新任务是否被安排  
  5.     boolean newTasksMayBeScheduled = true;  
  6.   
  7.     // 任务队列  
  8.     private TaskQueue queue;  
  9.   
  10.     TimerThread(TaskQueue queue) {  
  11.         this.queue = queue;  
  12.     }  
  13.   
  14.     public void run() {  
  15.         try {  
  16.             mainLoop();  
  17.         } finally {  
  18.             // Someone killed this Thread, behave as if Timer cancelled  
  19.             synchronized (queue) {  
  20.                 newTasksMayBeScheduled = false;  
  21.                 queue.clear(); // Eliminate obsolete references  
  22.             }  
  23.         }  
  24.     }  
  25.   
  26.     private void mainLoop() {  
  27.         while (true) {  
  28.             try {  
  29.                 TimerTask task;  
  30.                 boolean taskFired;  
  31.                 synchronized (queue) {  
  32.                     // Wait for queue to become non-empty  
  33.                     while (queue.isEmpty() && newTasksMayBeScheduled)  
  34.                         queue.wait();  
  35.                     if (queue.isEmpty())  
  36.                         break// Queue is empty and will forever remain; die  
  37.   
  38.                     // Queue nonempty; look at first evt and do the right thing  
  39.                     long currentTime, executionTime;  
  40.                     task = queue.getMin();  
  41.                     synchronized (task.lock) {  
  42.                         if (task.state == TimerTask.CANCELLED) {  
  43.                             queue.removeMin();  
  44.                             continue// No action required, poll queue again  
  45.                         }  
  46.                         currentTime = System.currentTimeMillis();  
  47.                         executionTime = task.nextExecutionTime;  
  48.                         if (taskFired = (executionTime <= currentTime)) {  
  49.                             if (task.period == 0) { // Non-repeating, remove  
  50.                                 queue.removeMin();  
  51.                                 task.state = TimerTask.EXECUTED;  
  52.                             } else { // Repeating task, reschedule  
  53.                                 queue  
  54.                                         .rescheduleMin(task.period < 0 ? currentTime  
  55.                                                 - task.period  
  56.                                                 : executionTime + task.period);  
  57.                             }  
  58.                         }  
  59.                     }  
  60.                     if (!taskFired) // Task hasn't yet fired; wait  
  61.                         queue.wait(executionTime - currentTime);  
  62.                 }  
  63.                 if (taskFired) // Task fired; run it, holding no locks  
  64.                     task.run();  
  65.             } catch (InterruptedException e) {  
  66.             }  
  67.         }  
  68.     }  

3.Timer类的主体和主要对外提供的方法 

Java代码  收藏代码
  1. import java.util.*;  
  2. import java.util.Date;  
  3.   
  4. public class Timer {  
  5.     // 定时任务队列  
  6.     private TaskQueue queue = new TaskQueue();  
  7.   
  8.     // 计时器线程  
  9.     private TimerThread thread = new TimerThread(queue);  
  10.   
  11.     private Object threadReaper = new Object() {  
  12.         protected void finalize() throws Throwable {  
  13.             synchronized (queue) {  
  14.                 thread.newTasksMayBeScheduled = false;  
  15.                 queue.notify(); // In case queue is empty.  
  16.             }  
  17.         }  
  18.     };  
  19.   
  20.     // ID号作为线程的ID  
  21.     private static int nextSerialNumber = 0;  
  22.   
  23.     private static synchronized int serialNumber() {  
  24.         return nextSerialNumber++;  
  25.     }  
  26.   
  27.     public Timer() {  
  28.         this("Timer-" + serialNumber());  
  29.     }  
  30.   
  31.     // 创建一个新计时器,可以指定其相关的线程作为守护程序运行。  
  32.     public Timer(boolean isDaemon) {  
  33.         this("Timer-" + serialNumber(), isDaemon);  
  34.     }  
  35.   
  36.     public Timer(String name) {  
  37.         thread.setName(name);  
  38.         thread.start();  
  39.     }  
  40.   
  41.     // 创建一个新计时器,其相关的线程具有指定的名称,并且可以指定作为守护程序运行。  
  42.     public Timer(String name, boolean isDaemon) {  
  43.         thread.setName(name);  
  44.         thread.setDaemon(isDaemon);  
  45.         thread.start();  
  46.     }  
  47.   
  48.     // 安排在指定延迟后执行指定的任务。时间单位毫秒  
  49.     public void schedule(TimerTask task, long delay) {  
  50.         if (delay < 0)  
  51.             throw new IllegalArgumentException("Negative delay.");  
  52.         sched(task, System.currentTimeMillis() + delay, 0);  
  53.     }  
  54.   
  55.     // 安排在指定的时间执行指定的任务。  
  56.     public void schedule(TimerTask task, Date time) {  
  57.         sched(task, time.getTime(), 0);  
  58.     }  
  59.   
  60.     // 安排指定的任务从指定的延迟后开始进行重复的固定延迟执行。  
  61.     public void schedule(TimerTask task, long delay, long period) {  
  62.         if (delay < 0)  
  63.             throw new IllegalArgumentException("Negative delay.");  
  64.         if (period <= 0)  
  65.             throw new IllegalArgumentException("Non-positive period.");  
  66.         sched(task, System.currentTimeMillis() + delay, -period);  
  67.     }  
  68.   
  69.     // 安排指定的任务在指定的时间开始进行重复的固定延迟执行。  
  70.     public void schedule(TimerTask task, Date firstTime, long period) {  
  71.         if (period <= 0)  
  72.             throw new IllegalArgumentException("Non-positive period.");  
  73.         sched(task, firstTime.getTime(), -period);  
  74.     }  
  75.   
  76.     // 安排指定的任务在指定的延迟后开始进行重复的固定速率执行。  
  77.     public void scheduleAtFixedRate(TimerTask task, long delay, long period) {  
  78.         if (delay < 0)  
  79.             throw new IllegalArgumentException("Negative delay.");  
  80.         if (period <= 0)  
  81.             throw new IllegalArgumentException("Non-positive period.");  
  82.         sched(task, System.currentTimeMillis() + delay, period);  
  83.     }  
  84.   
  85.     // 安排指定的任务在指定的时间开始进行重复的固定速率执行。  
  86.     public void scheduleAtFixedRate(TimerTask task, Date firstTime, long period) {  
  87.         if (period <= 0)  
  88.             throw new IllegalArgumentException("Non-positive period.");  
  89.         sched(task, firstTime.getTime(), period);  
  90.     }  
  91.     private void sched(TimerTask task, long time, long period) {  
  92.         if (time < 0)  
  93.             throw new IllegalArgumentException("Illegal execution time.");  
  94.         // 同步代码块 ,对queue的访问需要同步  
  95.         synchronized (queue) {  
  96.             if (!thread.newTasksMayBeScheduled)  
  97.                 throw new IllegalStateException("Timer already cancelled.");  
  98.             // 同步代码块,需要获得task的lock,锁  
  99.             synchronized (task.lock) {  
  100.                 if (task.state != TimerTask.VIRGIN)  
  101.                     throw new IllegalStateException(  
  102.                             "Task already scheduled or cancelled");  
  103.                 // 任务接下来执行的时刻  
  104.                 task.nextExecutionTime = time;  
  105.                 // 任务执行时间间隔周期  
  106.                 task.period = period;  
  107.                 // 任务已经安排,等待执行  
  108.                 task.state = TimerTask.SCHEDULED;  
  109.             }  
  110.             // 加入计时器等待任务队列  
  111.             queue.add(task);  
  112.             //  
  113.             if (queue.getMin() == task)  
  114.                 // 唤醒在此对象监视器上等待的单个线程。  
  115.                 queue.notify();  
  116.         }  
  117.     }  
  118.   
  119.     // 终止此计时器,丢弃所有当前已安排的任务。  
  120.     public void cancel() {  
  121.         synchronized (queue) {  
  122.             thread.newTasksMayBeScheduled = false;  
  123.             queue.clear();  
  124.             queue.notify(); // In case queue was already empty.  
  125.         }  
  126.     }  
  127.   
  128.     // 从此计时器的任务队列中移除所有已取消的任务。  
  129.     public int purge() {  
  130.         int result = 0;  
  131.   
  132.         synchronized (queue) {  
  133.             for (int i = queue.size(); i > 0; i--) {  
  134.                 if (queue.get(i).state == TimerTask.CANCELLED) {  
  135.                     queue.quickRemove(i);  
  136.                     result++;  
  137.                 }  
  138.             }  
  139.   
  140.             if (result != 0)  
  141.                 queue.heapify();  
  142.         }  
  143.   
  144.         return result;  
  145.     }  
  146. }  


  1. 用法:

Timer是调度控制器,TimerTask是可调度的任务:

   1: import java.util.Date;
   2: import java.util.TimerTask;
   3:  
   4: public class PlainTimerTask extends TimerTask {
   6:     @Override
   7:     public void run() {
   8:         System.out.println(new Date());
   9:  
  10:     }
  11:  
  12: }
  13:  

调度过程:

   1: import java.util.Timer;
   2:  
   3: public class TimerRunner {
   4:     
   5:     public static void main(String [] args){
   6:         Timer timer=new Timer();
   7:         timer.schedule(new PlainTimerTask(), 5000L);
   8:     }
   9:  
  10: }

2.原理:

     其基本处理模型是单线程调度的任务队列模型,Timer不停地接受调度任务,所有任务接受Timer调度后加入TaskQueue,TimerThread不停地去TaskQueue中取任务来执行.

 

Timer

 

     从图上不难看出,这就是生产者--消费者模型的一种特例:多生产者,单消费者模型。

     此种消息队列实现方式在浏览器中的编程模型中也有类似的实现,javascript中的定时执行函数setTimeout(expression,milliseconds)也是基于此种原理实现的。

     此种方式的不足之处为当某个任务执行时间较长,以致于超过了TaskQueue中下一个任务开始执行的时间,会影响整个任务执行的实时性。为了提高实时性,可以采用多个消费者一起消费来提高处理效率,避免此类问题的实现。

3.核心代码:

   1: private void mainLoop() {
   2:         while (true) {
   3:             try {
   4:                 TimerTask task;
   5:                 boolean taskFired;
   6:                 synchronized(queue) {
   7:                     // Wait for queue to become non-empty
   8:                     while (queue.isEmpty() && newTasksMayBeScheduled)
   9:                         queue.wait();
  10:                     if (queue.isEmpty())
  11:                         break; // Queue is empty and will forever remain; die
  12:  
  13:                     // Queue nonempty; look at first evt and do the right thing
  14:                     long currentTime, executionTime;
  15:                     task = queue.getMin();
  16:                     synchronized(task.lock) {
  17:                         if (task.state == TimerTask.CANCELLED) {
  18:                             queue.removeMin();
  19:                             continue;  // No action required, poll queue again
  20:                         }
  21:                         currentTime = System.currentTimeMillis();
  22:                         executionTime = task.nextExecutionTime;
  23:                         if (taskFired = (executionTime<=currentTime)) {
  24:                             if (task.period == 0) { // Non-repeating, remove
  25:                                 queue.removeMin();
  26:                                 task.state = TimerTask.EXECUTED;
  27:                             } else { // Repeating task, reschedule
  28:                                 queue.rescheduleMin(
  29:                                   task.period<0 ? currentTime   - task.period
  30:                                                 : executionTime + task.period);
  31:                             }
  32:                         }
  33:                     }
  34:                     if (!taskFired) // Task hasn't yet fired; wait
  35:                         queue.wait(executionTime - currentTime);
  36:                 }
  37:                 if (taskFired)  // Task fired; run it, holding no locks
  38:                     task.run();
  39:             } catch(InterruptedException e) {
  40:             }
  41:         }
  42:     }

1.先获得队列锁,然后去TaskQueue中取TimerTask,然后去判断此队列为空且新任务可安排标记是打开的。如果不满足,线程等待,将队列锁释放。

2.如果队列为空,那么跳出死循环。

3.取得队列中的下一个元素,并获得任务锁。

4.检查任务状态,如果任务状态为取消,那么直接取消,并跳过此轮循环。

5.得到任务的计划执行时间,并检查与当前时间的先后,如果当前时间已经到或者超过计划执行时间,那么置状态位为执行。

6.释放任务锁。

7.如果没有,线程等待执行时间和当前时间差。

8.释放队列锁

9.看任务是否可以执行标记,来确定是否执行任务

10反复从1开始








0 0
原创粉丝点击