Java中的Timer和Timer Task详解

Java Timer&TimerTask原理分析

如果你使用Java语言进行开发,对于定时执行任务这样的需求,自然而然会想到使用Timer和TimerTask完成任务,我最近就使用 Timer和TimerTask完成了一个定时执行的任务,实现得没有问题,但当在TimerTaks的run()方法中使用 Thread.sleep()方式时,可能会出现奇怪的现象,好像Timer失效了,网上查了一下,倒是有人遇到了相同的问题,但是并没有找到一篇解释为什么会出现这种情况,期待有某位达人能够分析清楚这个问题。

 

遇到了这样的问题,始终让我不爽,于是看了一下Timer的源码,先将了解到的内容整理如下,接下来再看看Thread.sleep()的源码,看能否找到问题所在。

 

在Java中,与定时任务执行相关的类很少,只有Timer、TimerTask、TimerThread、TaskQueue几个,其中每个类的职责大致如下:

Timer:一个Task的调度类,和TimerTask一样,暴露给最终用户使用的类,通过schedule方法安排Task的执行计划。该类通过TaskQueue和TimerThread类完成Task的调度。

TimerTask:实现Runnable接口,表明每一个任务均为一个独立的线程。通过run()方法提供用户定制自己任务。该类有一个比较重要的成员变量nextExecutionTime ,表示下一次执行该任务的时间。以后会看到,Timer机制就是靠这个值安排Task执行的。

TimerThread:继承于Thread,是真正执行Task的类。

TaskQueue:一个存储Task的数据结构,内部由一个最小堆实现,堆的每个成员为一个TimeTask,每个Task依靠其 nextExecutionTime值进行排序,也就是说,nextExecutionTime最小的任务在队列的最前端,从而能够现实最早执行。

 

要想使用Timer,用户只需要了解Timer和TimerTask,下面现已一个最基本的Timer和TimerTask使用案例入手,来看一下Timer内部的实现原理。

01 import java.util.Timer;
02
03 import java.util.TimerTask;
04
05 import org.junit.Test;
06
07   
08
09 class TestTimerTask extends TimerTask {
10
11   @Override
12
13   public void run() {
14
15     System.out.println("TestTimerTask is running......");
16
17   }
18
19 }
20
21 public class TimerTaskTest {
22
23   @Test
24
25   public void testTimerTask() {
26
27     Timer timer = new Timer();
28
29     timer.schedule(new TestTimerTask(), 010);
30
31   }
32
33 }

 

上面的代码是一个典型的Timer&TimerTask的应用,下面先来看一下new Timer()干了什么事,其源码如下:

public Timer(String name) {

        thread.setName(name);    //thread为TimerThread实例。

        thread.start();

}

从上面的源代码可以知道,创建Timer对象的同时也启动了TimerThread线程。下面来看看TimerThread干了什么事:

01 public void run() {
02
03         try {
04
05             mainLoop();                 //线程真正执行的代码在这个私有方法中
06
07         finally {
08
09             // Someone killed this Thread, behave as if Timer cancelled
10
11             synchronized(queue) {
12
13                 newTasksMayBeScheduled = false;
14
15                 queue.clear();  // Eliminate obsolete references
16
17             }
18
19         }
20
21 }

接着来看看私有方法mainLoop()干了什么事:

01 private void mainLoop() {
02
03         while (true) {
04
05             try {
06
07                 TimerTask task;
08
09                 boolean taskFired;       //是否已经到达Task的执行时间,如果已经到达,设置为true,否则置为false
10
11                 synchronized(queue) {
12
13                     // Wait for queue to become non-empty
14
15                     while (queue.isEmpty() && newTasksMayBeScheduled)
16
17                         queue.wait();                //由此可以看出,Timer通过wait & notify 方法安排线程之间的同步
18
19                     if (queue.isEmpty())
20
21                         break// Queue is empty and will forever remain; die
22
23   
24
25                     // Queue nonempty; look at first evt and do the right thing
26
27                     long currentTime, executionTime;
28
29                     task = queue.getMin();
30
31                     synchronized(task.lock) {
32
33                         if (task.state == TimerTask.CANCELLED) {
34
35                             queue.removeMin();
36
37                             continue;  // No action required, poll queue again
38
39                         }
40
41                         currentTime = System.currentTimeMillis();
42
43                         executionTime = task.nextExecutionTime;
44
45                         if (taskFired = (executionTime<=currentTime)) {        //Task的执行时间已到,设置taskFired为true
46
47                             if (task.period == 0) { // Non-repeating, remove
48
49                                 queue.removeMin();        //移除队列中的当前任务
50
51                                 task.state = TimerTask.EXECUTED;
52
53                             else // Repeating task, reschedule
54
55                                 queue.rescheduleMin(         //重新设置任务的下一次执行时间
56
57                                   task.period<0 ? currentTime   - task.period
58
59                                                 : executionTime + task.period);
60
61                             }
62
63                         }
64
65                     }
66
67                     if (!taskFired) // Task hasn't yet fired; wait
68
69                         queue.wait(executionTime - currentTime);    //还没有执行时间,通过wait等待特定时间
70
71                 }
72
73                 if (taskFired)  // Task fired; run it, holding no locks
74
75                     task.run();    //已经到达执行时间,执行任务
76
77             catch(InterruptedException e) {
78
79             }
80
81         }
82
83 }

也就是说,一旦创建了Timer类的实例,就一直存在一个循环在遍历queue中的任务,如果有任务的话,就通过thread去执行该任务,否则线程通过wait()方法阻塞自己,由于没有任务在队列中,就没有必要继续thread中的循环。

 

上面提到,如果Timer的任务队列中不包含任务时,Timer中的TimerThread线程并不会执行,接着来看看为Timer添加任务后会出现怎样的情况。为Timer添加任务就是timer.schedule()干的事,schedule()方法直接调用Timer的私有方法 sched(),sched()是真正安排Task的地方,其源代码如下:

01 private void sched(TimerTask task, long time, long period) {
02
03         if (time < 0)
04
05             throw new IllegalArgumentException("Illegal execution time.");
06
07   
08
09         synchronized(queue) {
10
11             if (!thread.newTasksMayBeScheduled)
12
13                 throw new IllegalStateException("Timer already cancelled.");
14
15   
16
17             synchronized(task.lock) {
18
19                 if (task.state != TimerTask.VIRGIN)             //我喜欢virgin状态,其他状态表明该Task已经被schedule过了
20
21                     throw new IllegalStateException(
22
23                         "Task already scheduled or cancelled");
24
25                  
26
27                  //设置Task下一次应该执行的时间, 由System.currentTimeMillis()+/-delay得到
28
29                 task.nextExecutionTime = time;              
30
31                 task.period = period;
32
33                 task.state = TimerTask.SCHEDULED;

你可能感兴趣的:(java,java,java,java)