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 }