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

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

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

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

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

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

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

优先级队列跟普通队列的最大差别就是,优先级队列每次出队的都是优先级最高的元素,并非按先进先出的方式。这里优先级队列的实现使用的是堆结构(当然,你也能够使用普通链表,可是每次出队得花O(n)的时间遍历链表找到优先级最大的元素,不划算)。插入及更新操作都能维持在O(logn):
  1. class TaskQueue {
  2. /**
  3. * Priority queue represented as a balanced binary heap: the two children
  4. * of queue[n] are queue[2*n] and queue[2*n+1]. The priority queue is
  5. * ordered on the nextExecutionTime field: The TimerTask with the lowest
  6. * nextExecutionTime is in queue[1] (assuming the queue is nonempty). For
  7. * each node n in the heap, and each descendant of n, d,
  8. * n.nextExecutionTime <= d.nextExecutionTime.
  9. */
  10. private TimerTask[] queue = new TimerTask[128];//使用数组存储堆元素,最大值128
  11. /**
  12. * The number of tasks in the priority queue. (The tasks are stored in
  13. * queue[1] up to queue[size]).
  14. */
  15. private int size = 0;//任务数
  16. /**
  17. * Returns the number of tasks currently on the queue.
  18. */
  19. int size() {
  20. return size;
  21. }
  22. /**
  23. * Adds a new task to the priority queue.
  24. */
  25. void add(TimerTask task) {//将TimerTask任务加入到此队列中
  26. // Grow backing store if necessary
  27. if (size + 1 == queue.length)
  28. queue = Arrays.copyOf(queue, 2*queue.length);
  29. queue[++size] = task;
  30. fixUp(size);//调整堆结构---->所谓的上滤
  31. }
  32. /**
  33. * Return the "head task" of the priority queue. (The head task is an
  34. * task with the lowest nextExecutionTime.)
  35. */
  36. TimerTask getMin() {//优先级最高的元素始终在第一个位置
  37. return queue[1];
  38. }
  39. /**
  40. * Return the ith task in the priority queue, where i ranges from 1 (the
  41. * head task, which is returned by getMin) to the number of tasks on the
  42. * queue, inclusive.
  43. */
  44. TimerTask get(int i) {
  45. return queue[i];
  46. }
  47. /**
  48. * Remove the head task from the priority queue.
  49. */
  50. void removeMin() {
  51. queue[1] = queue[size];
  52. queue[size--] = null; // Drop extra reference to prevent memory leak
  53. fixDown(1);//调整堆结构----->所谓的下滤
  54. }
  55. /**
  56. * Removes the ith element from queue without regard for maintaining
  57. * the heap invariant. Recall that queue is one-based, so
  58. * 1 <= i <= size.
  59. */
  60. void quickRemove(int i) {
  61. assert i <= size;
  62. queue[i] = queue[size];
  63. queue[size--] = null; // Drop extra ref to prevent memory leak
  64. }
  65. /**
  66. * Sets the nextExecutionTime associated with the head task to the
  67. * specified value, and adjusts priority queue accordingly.
  68. */
  69. void rescheduleMin(long newTime) {
  70. queue[1].nextExecutionTime = newTime;
  71. fixDown(1);
  72. }
  73. /**
  74. * Returns true if the priority queue contains no elements.
  75. */
  76. boolean isEmpty() {
  77. return size==0;
  78. }
  79. /**
  80. * Removes all elements from the priority queue.
  81. */
  82. void clear() {
  83. // Null out task references to prevent memory leak
  84. for (int i=1; i<=size; i++)
  85. queue[i] = null;
  86. size = 0;
  87. }
  88. /**
  89. * Establishes the heap invariant (described above) assuming the heap
  90. * satisfies the invariant except possibly for the leaf-node indexed by k
  91. * (which may have a nextExecutionTime less than its parent's).
  92. *
  93. * This method functions by "promoting" queue[k] up the hierarchy
  94. * (by swapping it with its parent) repeatedly until queue[k]'s
  95. * nextExecutionTime is greater than or equal to that of its parent.
  96. */
  97. private void fixUp(int k) {
  98. while (k > 1) {
  99. int j = k >> 1;
  100. if (queue[j].nextExecutionTime <= queue[k].nextExecutionTime)
  101. break;
  102. TimerTask tmp = queue[j]; queue[j] = queue[k]; queue[k] = tmp;
  103. k = j;
  104. }
  105. }
  106. /**
  107. * Establishes the heap invariant (described above) in the subtree
  108. * rooted at k, which is assumed to satisfy the heap invariant except
  109. * possibly for node k itself (which may have a nextExecutionTime greater
  110. * than its children's).
  111. *
  112. * This method functions by "demoting" queue[k] down the hierarchy
  113. * (by swapping it with its smaller child) repeatedly until queue[k]'s
  114. * nextExecutionTime is less than or equal to those of its children.
  115. */
  116. private void fixDown(int k) {
  117. int j;
  118. while ((j = k << 1) <= size && j > 0) {
  119. if (j < size &&
  120. queue[j].nextExecutionTime > queue[j+1].nextExecutionTime)
  121. j++; // j indexes smallest kid
  122. if (queue[k].nextExecutionTime <= queue[j].nextExecutionTime)
  123. break;
  124. TimerTask tmp = queue[j]; queue[j] = queue[k]; queue[k] = tmp;
  125. k = j;
  126. }
  127. }
  128. /**
  129. * Establishes the heap invariant (described above) in the entire tree,
  130. * assuming nothing about the order of the elements prior to the call.
  131. */
  132. void heapify() {//建堆操作,从第一个非叶子结点開始。
  133. for (int i = size/2; i >= 1; i--)
  134. fixDown(i);
  135. }
  136. }
TaskQueue内部是一个TimerTask数组。数组元素从1開始,这个数组就是所谓的堆,还是个小顶堆,堆顶元素
始终为第一个元素,每次加入TimerTask都会调用fixup上滤操作,维持堆的特性,每次删除堆顶元素后须要调用fixdown下滤操作。维持堆的特性。heapify是一个建堆函数(类似堆排序中的建堆操作),从第一个非叶子结点開始。


了解TaskQueue后,再看TimerThread类:
  1. class TimerThread extends Thread {
  2. /**
  3. * This flag is set to false by the reaper to inform us that there
  4. * are no more live references to our Timer object. Once this flag
  5. * is true and there are no more tasks in our queue, there is no
  6. * work left for us to do, so we terminate gracefully. Note that
  7. * this field is protected by queue's monitor!
  8. */
  9. boolean newTasksMayBeScheduled = true;
  10. /**
  11. * Our Timer's queue. We store this reference in preference to
  12. * a reference to the Timer so the reference graph remains acyclic.
  13. * Otherwise, the Timer would never be garbage-collected and this
  14. * thread would never go away.
  15. */
  16. private TaskQueue queue;//持有任务队列的引用
  17. TimerThread(TaskQueue queue) {
  18. this.queue = queue;
  19. }
  20. public void run() {
  21. try {
  22. mainLoop();//运行一个死循环,不断从队列中取出任务并运行,没有任务时会堵塞
  23. } finally {
  24. // Someone killed this Thread, behave as if Timer cancelled
  25. synchronized(queue) {
  26. newTasksMayBeScheduled = false;
  27. queue.clear(); // Eliminate obsolete references
  28. }
  29. }
  30. }
  31. /**
  32. * The main timer loop. (See class comment.)
  33. */
  34. private void mainLoop() {
  35. while (true) {//死循环
  36. try {
  37. TimerTask task;
  38. boolean taskFired;
  39. synchronized(queue) {//线程安全
  40. // Wait for queue to become non-empty
  41. while (queue.isEmpty() && newTasksMayBeScheduled)//没有任务时
  42. queue.wait();//等待
  43. if (queue.isEmpty())
  44. break; // Queue is empty and will forever remain; die
  45. // Queue nonempty; look at first evt and do the right thing
  46. long currentTime, executionTime;
  47. task = queue.getMin();//取出优先级最高的任务
  48. synchronized(task.lock) {
  49. if (task.state == TimerTask.CANCELLED) {//任务被取消
  50. queue.removeMin();//干掉这个任务
  51. continue; // No action required, poll queue again
  52. }
  53. currentTime = System.currentTimeMillis();
  54. executionTime = task.nextExecutionTime;
  55. if (taskFired = (executionTime<=currentTime)) {//任务是否已经运行过了
  56. if (task.period == 0) { // Non-repeating, remove
  57. queue.removeMin();//已经运行过的任务会从队列中移除
  58. task.state = TimerTask.EXECUTED;
  59. } else { // Repeating task, reschedule
  60. queue.rescheduleMin(
  61. task.period<0 ?
  62.  
  63. currentTime - task.period
  64. : executionTime + task.period);
  65. }
  66. }
  67. }
  68. if (!taskFired) // Task hasn't yet fired; wait
  69. queue.wait(executionTime - currentTime);//没到运行时间久等待
  70. }
  71. if (taskFired) // Task fired; run it, holding no locks
  72. task.run();//运行该任务
  73. } catch(InterruptedException e) {
  74. }
  75. }
  76. }
  77. }

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

  1. private void sched(TimerTask task, long time, long period) {//全部的schedule方法都会调用此方法
  2. if (time < 0)
  3. throw new IllegalArgumentException("Illegal execution time.");
  4. // Constrain value of period sufficiently to prevent numeric
  5. // overflow while still being effectively infinitely large.
  6. if (Math.abs(period) > (Long.MAX_VALUE >> 1))
  7. period >>= 1;
  8. synchronized(queue) {
  9. if (!thread.newTasksMayBeScheduled)
  10. throw new IllegalStateException("Timer already cancelled.");
  11. synchronized(task.lock) {
  12. if (task.state != TimerTask.VIRGIN)
  13. throw new IllegalStateException(
  14. "Task already scheduled or cancelled");
  15. task.nextExecutionTime = time;
  16. task.period = period;
  17. task.state = TimerTask.SCHEDULED;
  18. }
  19. queue.add(task);//增加任务队列
  20. if (queue.getMin() == task)
  21. queue.notify();//唤醒任务运行线程
  22. }
  23. }

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

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

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

  1. public Timer(boolean isDaemon) {
  2. this("Timer-" + serialNumber(), isDaemon);
  3. }

以上就是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. 【LeetCode-面试算法经典-Java实现】【130-Surrounded Regions(围绕区域)】

    [130-Surrounded Regions(围绕区域)] [LeetCode-面试算法经典-Java实现][全部题目文件夹索引] 原题 Given a 2D board containing 'X ...

  2. Windows/Linux下磁盘使用的图形化工具简介

    Windows/Linux下磁盘使用的图形化工具简介   如何以图形界面查看磁盘及分区的大小及剩余容量呢?今天我为大家介绍两款Windows/Linux下磁盘使用的图形化工具分别是Spacesniff ...

  3. Codefroces 760 B. Frodo and pillows

    B. Frodo and pillows time limit per test 1 second memory limit per test 256 megabytes input standard ...

  4. Android Studio com.android.dex.DexException: Multiple dex files define(重复引用包)

    如果你用Android Studio开发,并且要用其他项目作为library,这个问题是很容易出现的.出现这个问题的原因是包的重复引用,意思就是在你自己的项目中引用了某个包,而被你作为library的 ...

  5. 【hdu 6000】Wash

    [链接] 我是链接,点我呀:) [题意] 在这里输入题意 [题解] 因为每件衣服都是没有区别的. 只有洗衣机不同会影响洗衣时间. 那么我们把每台洗衣机洗衣的时间一开始都加入到队列中. 比如{2,3,6 ...

  6. 关于PyYAML报错问题解决

    转自:http://www.fwqtg.net/%E5%85%B3%E4%BA%8Epyyaml%E6%8A%A5%E9%94%99%E9%97%AE%E9%A2%98%E8%A7%A3%E5%86% ...

  7. (最新)使用爬虫刷CSDN博客访问量——亲测有效

    说明:该篇博客是博主一字一码编写的,实属不易,请尊重原创,谢谢大家! 1.概述 前言:前两天刚写了第一篇博客https://blog.csdn.net/qq_41782425/article/deta ...

  8. GPUImage ==> 一个基于GPU图像和视频处理的开源iOS框架

    Logo 项目介绍: GPUImage是Brad Larson在github托管的开源项目. GPUImage是一个基于GPU图像和视频处理的开源iOS框架,提供各种各样的图像处理滤镜,并且支持照相机 ...

  9. GO语言学习(四)GO语言语言结构

    Go Hello World 实例 Go 语言的基础组成有以下几个部分: 包声明 引入包 函数 变量 语句 & 表达式 注释 接下来让我们来看下简单的代码,该代码输出了"Hello ...

  10. asp.net获取客户真实ip非代理ip:

    public string GetUserIP()   {       string _userIP;       if(Request.ServerVariables["HTTP_VIA& ...