Timer是java.util包中的一个工具类,提供了定时器的功能。

我们能够构造一个Timer对象,然后调用其schedule方法在某个特定的时间或者若干延时之后去运行一个特定的任务。甚至你能够让其以特定频率一直运行某个任务,这个任务用TimerTask描写叙述,我们将须要的操作写在TimerTask类的run方法中就可以。

本着“知其然。知其所以然”的心态,我决定研究下这个类的源代码。

打开Timer类的源代码我发现了这样两个成员变量:

 /**
* The timer task queue. This data structure is shared with the timer
* thread. The timer produces tasks, via its various schedule calls,
* and the timer thread consumes, executing timer tasks as appropriate,
* and removing them from the queue when they're obsolete.
*/
private final TaskQueue queue = new TaskQueue();//任务队列
/**
* The timer thread.
*/
private final TimerThread thread = new TimerThread(queue);//运行线程

TaskQueue是一个优先级队列。存放了我们将要运行的TimerTask对象。TimerTask对象是通过Timer类的一系列schedule方法增加队列的,TimerThread负责不断取出TaskQueue中的任务,然后运行之,也就是说。全部的任务都是是在子线程中运行的。TaskQueue队列是以其下次运行时间的先后排序的,TimerThread每次取出的都是须要最先运行的TimerTask。

(跟android中的Handler机制非常类似~)

优先级队列跟普通队列的最大差别就是,优先级队列每次出队的都是优先级最高的元素,并非按先进先出的方式。这里优先级队列的实现使用的是堆结构(当然,你也能够使用普通链表,可是每次出队得花O(n)的时间遍历链表找到优先级最大的元素,不划算)。插入及更新操作都能维持在O(logn):
class TaskQueue {
/**
* Priority queue represented as a balanced binary heap: the two children
* of queue[n] are queue[2*n] and queue[2*n+1]. The priority queue is
* ordered on the nextExecutionTime field: The TimerTask with the lowest
* nextExecutionTime is in queue[1] (assuming the queue is nonempty). For
* each node n in the heap, and each descendant of n, d,
* n.nextExecutionTime <= d.nextExecutionTime.
*/
private TimerTask[] queue = new TimerTask[128];//使用数组存储堆元素,最大值128
/**
* The number of tasks in the priority queue. (The tasks are stored in
* queue[1] up to queue[size]).
*/
private int size = 0;//任务数
/**
* Returns the number of tasks currently on the queue.
*/
int size() {
return size;
}
/**
* Adds a new task to the priority queue.
*/
void add(TimerTask task) {//将TimerTask任务加入到此队列中
// Grow backing store if necessary
if (size + 1 == queue.length)
queue = Arrays.copyOf(queue, 2*queue.length);
queue[++size] = task;
fixUp(size);//调整堆结构---->所谓的上滤
}
/**
* Return the "head task" of the priority queue. (The head task is an
* task with the lowest nextExecutionTime.)
*/
TimerTask getMin() {//优先级最高的元素始终在第一个位置
return queue[1];
}
/**
* Return the ith task in the priority queue, where i ranges from 1 (the
* head task, which is returned by getMin) to the number of tasks on the
* queue, inclusive.
*/
TimerTask get(int i) {
return queue[i];
}
/**
* Remove the head task from the priority queue.
*/
void removeMin() {
queue[1] = queue[size];
queue[size--] = null; // Drop extra reference to prevent memory leak
fixDown(1);//调整堆结构----->所谓的下滤
}
/**
* Removes the ith element from queue without regard for maintaining
* the heap invariant. Recall that queue is one-based, so
* 1 <= i <= size.
*/
void quickRemove(int i) {
assert i <= size;
queue[i] = queue[size];
queue[size--] = null; // Drop extra ref to prevent memory leak
}
/**
* Sets the nextExecutionTime associated with the head task to the
* specified value, and adjusts priority queue accordingly.
*/
void rescheduleMin(long newTime) {
queue[1].nextExecutionTime = newTime;
fixDown(1);
}
/**
* Returns true if the priority queue contains no elements.
*/
boolean isEmpty() {
return size==0;
}
/**
* Removes all elements from the priority queue.
*/
void clear() {
// Null out task references to prevent memory leak
for (int i=1; i<=size; i++)
queue[i] = null;
size = 0;
}
/**
* Establishes the heap invariant (described above) assuming the heap
* satisfies the invariant except possibly for the leaf-node indexed by k
* (which may have a nextExecutionTime less than its parent's).
*
* This method functions by "promoting" queue[k] up the hierarchy
* (by swapping it with its parent) repeatedly until queue[k]'s
* nextExecutionTime is greater than or equal to that of its parent.
*/
private void fixUp(int k) {
while (k > 1) {
int j = k >> 1;
if (queue[j].nextExecutionTime <= queue[k].nextExecutionTime)
break;
TimerTask tmp = queue[j]; queue[j] = queue[k]; queue[k] = tmp;
k = j;
}
}
/**
* Establishes the heap invariant (described above) in the subtree
* rooted at k, which is assumed to satisfy the heap invariant except
* possibly for node k itself (which may have a nextExecutionTime greater
* than its children's).
*
* This method functions by "demoting" queue[k] down the hierarchy
* (by swapping it with its smaller child) repeatedly until queue[k]'s
* nextExecutionTime is less than or equal to those of its children.
*/
private void fixDown(int k) {
int j;
while ((j = k << 1) <= size && j > 0) {
if (j < size &&
queue[j].nextExecutionTime > queue[j+1].nextExecutionTime)
j++; // j indexes smallest kid
if (queue[k].nextExecutionTime <= queue[j].nextExecutionTime)
break;
TimerTask tmp = queue[j]; queue[j] = queue[k]; queue[k] = tmp;
k = j;
}
}
/**
* Establishes the heap invariant (described above) in the entire tree,
* assuming nothing about the order of the elements prior to the call.
*/
void heapify() {//建堆操作,从第一个非叶子结点開始。
for (int i = size/2; i >= 1; i--)
fixDown(i);
}
}
TaskQueue内部是一个TimerTask数组。数组元素从1開始,这个数组就是所谓的堆,还是个小顶堆,堆顶元素
始终为第一个元素,每次加入TimerTask都会调用fixup上滤操作,维持堆的特性,每次删除堆顶元素后须要调用fixdown下滤操作。维持堆的特性。heapify是一个建堆函数(类似堆排序中的建堆操作),从第一个非叶子结点開始。


了解TaskQueue后,再看TimerThread类:
class TimerThread extends Thread {
/**
* This flag is set to false by the reaper to inform us that there
* are no more live references to our Timer object. Once this flag
* is true and there are no more tasks in our queue, there is no
* work left for us to do, so we terminate gracefully. Note that
* this field is protected by queue's monitor!
*/
boolean newTasksMayBeScheduled = true;
/**
* Our Timer's queue. We store this reference in preference to
* a reference to the Timer so the reference graph remains acyclic.
* Otherwise, the Timer would never be garbage-collected and this
* thread would never go away.
*/
private TaskQueue queue;//持有任务队列的引用
TimerThread(TaskQueue queue) {
this.queue = queue;
}
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
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) { // Non-repeating, remove
queue.removeMin();//已经运行过的任务会从队列中移除
task.state = TimerTask.EXECUTED;
} else { // Repeating task, reschedule
queue.rescheduleMin(
task.period<0 ? currentTime - task.period
: executionTime + task.period);
}
}
}
if (!taskFired) // Task hasn't yet fired; wait
queue.wait(executionTime - currentTime);//没到运行时间久等待
}
if (taskFired) // Task fired; run it, holding no locks
task.run();//运行该任务
} catch(InterruptedException e) {
}
}
}
}

凝视写的非常明确。TimerThread会在run方法中调用mainloop方法。这是一个死循环,不断从任务队列中取出任务。运行之,假设没有任务可运行,将会wait,等待队列非空,而Timer类的schedule方法会调用notify唤醒该线程。运行任务。

private void sched(TimerTask task, long time, long period) {//全部的schedule方法都会调用此方法
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();//唤醒任务运行线程
}
}

那么TimerThread何时被启动的呢?猜猜也能知道,肯定是Timer被创建时运行的:

public Timer(String name) {
thread.setName(name);
thread.start();//启动线程
}

当我们主线程运行完成后。Timer线程可能仍然处于堵塞或者其它状态,有时这不是我们希望看到的,Timer类有这样一个构造器,能够让任务运行线程以守护线程的方式运行。这样当主线程运行完成后。守护线程也会停止。

 public Timer(boolean isDaemon) {
this("Timer-" + serialNumber(), isDaemon);
}

以上就是Timer类的源代码分析过程。最后贴上一张图。帮助理解:

【源代码】Timer和TimerTask源代码剖析的更多相关文章

  1. Java并发编程:Timer和TimerTask(转载)

    Java并发编程:Timer和TimerTask(转载) 下面内容转载自: http://blog.csdn.net/xieyuooo/article/details/8607220 其实就Timer ...

  2. Java线程:Timer和TimerTask

    Timer和TimerTask可以做为实现线程的第三种方式,前两中方式分别是继承自Thread类和实现Runnable接口. Timer是一种线程设施,用于安排以后在后台线程中执行的任务.可安排任务执 ...

  3. Java中的Timer和TimerTask在Android中的用法(转)

    转自:http://blog.csdn.net/zuolongsnail/article/details/8168689 在开发中我们有时会有这样的需求,即在固定的每隔一段时间执行某一个任务.比如UI ...

  4. Java定时任务Timer、TimerTask与ScheduledThreadPoolExecutor详解

     定时任务就是在指定时间执行程序,或周期性执行计划任务.Java中实现定时任务的方法有很多,本文从从JDK自带的一些方法来实现定时任务的需求. 一.Timer和TimerTask  Timer和Tim ...

  5. JDK中的Timer和TimerTask详解(zhuan)

    http://www.cnblogs.com/lingiu/p/3782813.html ************************************************** 目录结构 ...

  6. Timer与TimerTask的真正原理&使用介绍

    转载: Timer与TimerTask的真正原理&使用介绍 其实就Timer来讲就是一个调度器,而TimerTask呢只是一个实现了run方法的一个类,而具体的TimerTask需要由你自己来 ...

  7. Java并发编程:Timer和TimerTask

    Java并发编程:Timer和TimerTask 下面内容转载自: http://blog.csdn.net/xieyuooo/article/details/8607220 其实就Timer来讲就是 ...

  8. android Timer and TImerTask

    android Timer and TImerTask Caused by: java.lang.IllegalStateException: TimerTask is scheduled alrea ...

  9. java中计时器的用法Timer和TimerTask的用法__java中利用Timer与TImerTask 计时器间隔执行任务

          经常我们都会有这样的需求,要固定的每隔一段时间执行某一个任务.比如:   我们做一个缓存来减少与数据库的交互,而为了使缓存与数据库中的数据尽量达到同步,需要每个固定的一段时间去数据库中的数 ...

随机推荐

  1. stm32与arm7比较(经典)

    http://wenku.baidu.com/link?url=LIVcT1AQL0IgVF1xan5Zy9rXarCBo66hj7OXSxM1ap7FpssO4c3sd1pZd8azfBPr3PBy ...

  2. 2.技巧: 用 JAXM 发送和接收 SOAP 消息—Java API 使许多手工生成和发送消息方面必需的步骤自动化

    转自:https://www.cnblogs.com/chenying99/archive/2013/05/23/3094128.html 技巧: 用 JAXM 发送和接收 SOAP 消息—Java ...

  3. 洛谷——V1772 巧妙填数

    描述 将1,2,\cdots,91,2,⋯,9共99个数分成三组,分别组成三个三位数,且使这三个三位数构成1:2:31:2:3的比例. 试求出所有满足条件的三个三位数.例如:三个三位数192,384, ...

  4. Arch Linux实体机安装记录

    下面将记录笔者在戴尔笔记本安装arch linux的过程,用于记录,以便下次使用. 本文的内容参考arch linux官方Wiki. 首先,使用Power ISO把镜像安装到U盘,使用U盘安装. 通过 ...

  5. 看好腾讯,鄙视百度(腾讯的核心竞争力,不是超过10亿的QQ的注册用户,也不是某一项产品、技术方面优势,而是“耐心”:懂得在合适的时间推出合适的产品。”)

    百度,自始至终只是一个低劣的模仿者,且一切向前看,完全违背了一个搜索引擎所应该遵循的基本原则.谁给的钱多就能搜着谁,这跟贩毒有什么区别? 腾讯也在模仿别人,但是,它是模仿然后超越.在中国互联网发展历史 ...

  6. hard-negative mining 及伪代码实现

    Histogram of Oriented Gradients and Object Detection 获得 records 对于目标检测(object detection)问题,所谓的 hard- ...

  7. Web应用开发(Servlet+html+Mysql)入门小示例

    在安装好eclipse并配置完安装环境.安装好tomcat的前提下: 1.新建Dynamic Web Project,选择好运行的tomcat服务器版本等:2.在WebContent下:   新建fo ...

  8. 【例题 7-5 UVA - 129】Krypton Factor

    [链接] 我是链接,点我呀:) [题意] 在这里输入题意 [题解] 每次枚举增加一个字符; 然后看看新生成的字符的后缀里面有没有出现连续子串就好,前面已经确认过的没必要重复确认 (枚举长度为偶数的一个 ...

  9. Mycat 读写分离+分库分表

    上次进过GTID复制的学习记录,已经搭建好了主从复制的服务器,现在利用现有的主从复制环境,加上正在研究的Mycat,实现了主流分布式数据库的测试 Mycat就不用多介绍了,可以实现很多分布式数据库的功 ...

  10. 哈夫曼树的介绍 ---java实现

    一.     什么是哈夫曼树 是一种带权路径长度最短的二叉树,也称最优二叉树 带权路径长度:WPL=(W1*L1+W2*L2+W3*L3+...+ Wn*Ln) N个权值Wi(i=1,2,...n)构 ...