4、定时器Timer

虽然Timer这个类已经很少用,在实际项目当中都是用第三方定时器项目,如quartz。

但是如果研究一下Timer的源码,还是有必要的,因为其中包含了涉及的知识点还是挺多的。比如,自动扩容、优先级队列,还有任务调度的一些策略。

 1 /**
 2      * 在特定的时间调度特定的任务。 如果
 3      *时间是一个过去的时间,那么任务会立即被调度
 4      *
 5      * @param task 将被调度的任务
 6      * @param time 任务被执行的时间
 7      * @throws IllegalArgumentException 如果time.getTime()是负数
 8      * @throws IllegalStateException 如果任务已经被调度
 9      *   或者取消, timer 被取消, or timer 的线程被终止了
10      * @throws NullPointerException 如果timer或者task为null
11      */
12     public void schedule(TimerTask task, Date time) {
13         sched(task, time.getTime(), 0);
14     }
 1  /**
 2      * 在特定的时间间隔执行特定的任务,时间单位为毫秒。如果时间间隔
 3      * 是正整数,任务将会以这个频率反复进行调度。如果时间间隔是0,
 4      * 那么任务只会被调度一次。时间是以Date.getTime()形式给出的。
 5      * 这个方法是检查timer的状态、task状态和初始的执行时间,但是不
 6      * 是时间间隔。
 7      *
 8      * @throws IllegalArgumentException 如果time是负值
 9      * @throws IllegalStateException 如果任务已经调度
10      *    取消、timer已经取消或者timer线程被终止了。
11      * @throws NullPointerException task为null
12      */
13     private void sched(TimerTask task, long time, long period) {
14         if (time < 0)
15             throw new IllegalArgumentException("Illegal execution time.");
16 
17         //约束时间间隔,有效的防止了数值溢出
18        //因为当时间间隔数值无限大时,它只是取这个值的平方根,与
19        //Long.MAX_VALUE值的一半进行比较,如果比这个值大,说明
20        //时间间隔数值已经溢出。
21         if (Math.abs(period) > (Long.MAX_VALUE >> 1))
22             period >>= 1;
23 
24         synchronized(queue) {
25             if (!thread.newTasksMayBeScheduled)
26                 throw new IllegalStateException("Timer already cancelled.");
27 
28             synchronized(task.lock) {
29                 if (task.state != TimerTask.VIRGIN)
30                     throw new IllegalStateException(
31                         "Task already scheduled or cancelled");
32                 task.nextExecutionTime = time;
33                 task.period = period;
34                 task.state = TimerTask.SCHEDULED;
35             }
36 
37             queue.add(task);
38             if (queue.getMin() == task)
39                 queue.notify();
40         }
41     }
 1  /**
 2      * 将新任务加入到优先级队列。其实queue就是一个初始长度为128的数组。当新加入的数组元素导致数组长度不够时,此时会将数组长度变成原来的2倍,并且将新任务加到数组当中。
 3    
 4      */
 5     void add(TimerTask task) {
 6         // Grow backing store if necessary
 7         if (size + 1 == queue.length)
 8             queue = Arrays.copyOf(queue, 2*queue.length);
 9 
10         queue[++size] = task;
11         fixUp(size);
12     }
 1  /**
 2      * 此时k是队列中所放任务的个数。也就表示新放入的任务。
 3      * 假设先前的堆已经是一个小根堆,那么此时新放入的元素k
 4      * 有可能打破这种局面(也就此时节点k的nextExecutionTime
 5      * 有可能比它的父节点要小)。于是这个方法就是对这个堆进行调整。
 6      */
 7     private void fixUp(int k) {
 8         while (k > 1) {
 9             int j = k >> 1; //j指向k的父元素
10             if (queue[j].nextExecutionTime <= queue[k].nextExecutionTime)
11                 break;
12             TimerTask tmp = queue[j];  queue[j] = queue[k]; queue[k] = tmp;
13             k = j;
14         }
15     }
 1 class TimerThread extends Thread {
 2   private TaskQueue queue;
 3 
 4     TimerThread(TaskQueue queue) {
 5         this.queue = queue;
 6     }
 7 
 8     public void run() {
 9         try {
10             mainLoop();
11         } finally {
12             // 如果某人杀死了这个线程就和Timer被取消一样
13             synchronized(queue) {
14                 newTasksMayBeScheduled = false;
15                 queue.clear();  //清除过时的引用
16             }
17         }
18     }
19 
20       private void mainLoop() {
21         while (true) {
22             try {
23                 TimerTask task;
24                 boolean taskFired;
25                 synchronized(queue) {
26                     // Wait for queue to become non-empty
27                     while (queue.isEmpty() && newTasksMayBeScheduled)
28                         queue.wait();//如果队列是空的,而且新任务也许被调度了,那么线程就交出锁进行等待。
29                     if (queue.isEmpty())
30                         break;//如果队列确实为空,那么保持死亡状态。
31 
32                     // 队列不为空的情况
33                     long currentTime, executionTime;
34                     task = queue.getMin();
35                     synchronized(task.lock) {
36                         if (task.state == TimerTask.CANCELLED) {
37                             queue.removeMin();
38                             continue;  // 没有行为要去,那么继续扫描队列
39                         }
40                         currentTime = System.currentTimeMillis();
41                         executionTime = task.nextExecutionTime;
42                         if (taskFired = (executionTime<=currentTime)) {
43                             if (task.period == 0) { // 不重复
44                                 queue.removeMin();
45                                 task.state = TimerTask.EXECUTED;
46                             } else { // 重复调度任务
47                                 queue.rescheduleMin(
48                                   task.period<0 ? currentTime   - task.period
49                                                 : executionTime + task.period);
50                             }
51                         }
52                     }
53                     if (!taskFired) //任务还没有被触发并且还没有到指定的时间,那么队列处于阻塞状态
54                         queue.wait(executionTime - currentTime);
55                 }
56                 if (taskFired)  //如果任务已经触发,那么执行任务体
57                     task.run();
58             } catch(InterruptedException e) {
59             }
60         }
61     }
62 }

 

posted on 2015-05-26 18:17  飞机说之代码也疯狂  阅读(230)  评论(0编辑  收藏  举报