天天看點

Timer&TimerTask原理分析

轉載位址,請珍惜作者的勞動成果,轉載請注明出處:http://www.open-open.com/lib/view/open1337176725619.html

如果你使用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内部的實作原理。

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

<code>import</code> <code>java.util.Timer;</code>

<code>import</code> <code>java.util.TimerTask;</code>

<code>import</code> <code>org.junit.Test;</code>

<code> </code> 

<code>class</code> <code>TestTimerTask </code><code>extends</code> <code>TimerTask {</code>

<code>  </code><code>@Override</code>

<code>  </code><code>public</code> <code>void</code> <code>run() {</code>

<code>    </code><code>System.out.println(</code><code>"TestTimerTask is running......"</code><code>);</code>

<code>  </code><code>}</code>

<code>}</code>

<code>public</code> <code>class</code> <code>TimerTaskTest {</code>

<code>  </code><code>@Test</code>

<code>  </code><code>public</code> <code>void</code> <code>testTimerTask() {</code>

<code>    </code><code>Timer timer = </code><code>new</code> <code>Timer();</code>

<code>    </code><code>timer.schedule(</code><code>new</code> <code>TestTimerTask(), </code><code>0</code><code>, </code><code>10</code><code>);</code>

  

上面的代碼是一個典型的Timer&amp;TimerTask的應用,下面先來看一下new Timer()幹了什麼事,其源碼如下:

public Timer(String name) {

        thread.setName(name);    //thread為TimerThread執行個體。

        thread.start();

}

從上面的源代碼可以知道,建立Timer對象的同時也啟動了TimerThread線程。下面來看看TimerThread幹了什麼事:

<code>public</code> <code>void</code> <code>run() {</code>

<code>        </code><code>try</code> <code>{</code>

<code>            </code><code>mainLoop();                 </code><code>//線程真正執行的代碼在這個私有方法中</code>

<code>        </code><code>} </code><code>finally</code> <code>{</code>

<code>            </code><code>// Someone killed this Thread, behave as if Timer cancelled</code>

<code>            </code><code>synchronized</code><code>(queue) {</code>

<code>                </code><code>newTasksMayBeScheduled = </code><code>false</code><code>;</code>

<code>                </code><code>queue.clear();  </code><code>// Eliminate obsolete references</code>

<code>            </code><code>}</code>

<code>        </code><code>}</code>

  接着來看看私有方法mainLoop()幹了什麼事:

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69

70

71

72

73

74

75

76

77

78

79

80

81

82

83

<code>private</code> <code>void</code> <code>mainLoop() {</code>

<code>        </code><code>while</code> <code>(</code><code>true</code><code>) {</code>

<code>            </code><code>try</code> <code>{</code>

<code>                </code><code>TimerTask task;</code>

<code>                </code><code>boolean</code> <code>taskFired;       </code><code>//是否已經到達Task的執行時間,如果已經到達,設定為true,否則置為false</code>

<code>                </code><code>synchronized</code><code>(queue) {</code>

<code>                    </code><code>// Wait for queue to become non-empty</code>

<code>                    </code><code>while</code> <code>(queue.isEmpty() &amp;&amp; newTasksMayBeScheduled)</code>

<code>                        </code><code>queue.wait();                </code><code>//由此可以看出,Timer通過wait &amp; notify 方法安排線程之間的同步</code>

<code>                    </code><code>if</code> <code>(queue.isEmpty())</code>

<code>                        </code><code>break</code><code>; </code><code>// Queue is empty and will forever remain; die</code>

<code>                    </code><code>// Queue nonempty; look at first evt and do the right thing</code>

<code>                    </code><code>long</code> <code>currentTime, executionTime;</code>

<code>                    </code><code>task = queue.getMin();</code>

<code>                    </code><code>synchronized</code><code>(task.lock) {</code>

<code>                        </code><code>if</code> <code>(task.state == TimerTask.CANCELLED) {</code>

<code>                            </code><code>queue.removeMin();</code>

<code>                            </code><code>continue</code><code>;  </code><code>// No action required, poll queue again</code>

<code>                        </code><code>}</code>

<code>                        </code><code>currentTime = System.currentTimeMillis();</code>

<code>                        </code><code>executionTime = task.nextExecutionTime;</code>

<code>                        </code><code>if</code> <code>(taskFired = (executionTime&lt;=currentTime)) {        </code><code>//Task的執行時間已到,設定taskFired為true</code>

<code>                            </code><code>if</code> <code>(task.period == </code><code>0</code><code>) { </code><code>// Non-repeating, remove</code>

<code>                                </code><code>queue.removeMin();        </code><code>//移除隊列中的目前任務</code>

<code>                                </code><code>task.state = TimerTask.EXECUTED;</code>

<code>                            </code><code>} </code><code>else</code> <code>{ </code><code>// Repeating task, reschedule</code>

<code>                                </code><code>queue.rescheduleMin(         </code><code>//重新設定任務的下一次執行時間</code>

<code>                                  </code><code>task.period&lt;</code><code>0</code> <code>? currentTime   - task.period</code>

<code>                                                </code><code>: executionTime + task.period);</code>

<code>                            </code><code>}</code>

<code>                    </code><code>}</code>

<code>                    </code><code>if</code> <code>(!taskFired) </code><code>// Task hasn't yet fired; wait</code>

<code>                        </code><code>queue.wait(executionTime - currentTime);    </code><code>//還沒有執行時間,通過wait等待特定時間</code>

<code>                </code><code>}</code>

<code>                </code><code>if</code> <code>(taskFired)  </code><code>// Task fired; run it, holding no locks</code>

<code>                    </code><code>task.run();    </code><code>//已經到達執行時間,執行任務</code>

<code>            </code><code>} </code><code>catch</code><code>(InterruptedException e) {</code>

也就是說,一旦建立了Timer類的執行個體,就一直存在一個循環在周遊queue中的任務,如果有任務的話,就通過thread去執行該任務,否則線程通過wait()方法阻塞自己,由于沒有任務在隊列中,就沒有必要繼續thread中的循環。

上面提到,如果Timer的任務隊列中不包含任務時,Timer中的TimerThread線程并不會執行,接着來看看為Timer添加任務後會出現怎樣的情況。為Timer添加任務就是timer.schedule()幹的事,schedule()方法直接調用Timer的私有方法 sched(),sched()是真正安排Task的地方,其源代碼如下:

<code>private</code> <code>void</code> <code>sched(TimerTask task, </code><code>long</code> <code>time, </code><code>long</code> <code>period) {</code>

<code>        </code><code>if</code> <code>(time &lt; </code><code>0</code><code>)</code>

<code>            </code><code>throw</code> <code>new</code> <code>IllegalArgumentException(</code><code>"Illegal execution time."</code><code>);</code>

<code>        </code><code>synchronized</code><code>(queue) {</code>

<code>            </code><code>if</code> <code>(!thread.newTasksMayBeScheduled)</code>

<code>                </code><code>throw</code> <code>new</code> <code>IllegalStateException(</code><code>"Timer already cancelled."</code><code>);</code>

<code>            </code><code>synchronized</code><code>(task.lock) {</code>

<code>                </code><code>if</code> <code>(task.state != TimerTask.VIRGIN)             </code><code>//我喜歡virgin狀态,其他狀态表明該Task已經被schedule過了</code>

<code>                    </code><code>throw</code> <code>new</code> <code>IllegalStateException(</code>

<code>                        </code><code>"Task already scheduled or cancelled"</code><code>);</code>

<code>                </code> 

<code>                 </code><code>//設定Task下一次應該執行的時間, 由System.currentTimeMillis()+/-delay得到</code>

<code>                </code><code>task.nextExecutionTime = time;              </code>

<code>                </code><code>task.period = period;</code>

<code>                </code><code>task.state = TimerTask.SCHEDULED;</code>

<code>            </code><code>queue.add(task);            </code><code>//queue為TaskQueue類的執行個體,添加任務到隊列中</code>

<code>            </code><code>if</code> <code>(queue.getMin() == task)        </code><code>//擷取隊列中nextExecutionTime最小的任務,如果與目前任務相同</code>

<code>                </code><code>queue.notify();                         </code><code>//還記得前面看到的queue.wait()方法麼</code>

不要奇怪,為什麼要判斷queue.getMin() == task時,才通過queue.notify()恢複執行。因為這種方式已經滿足所有的喚醒要求了。

如果安排目前Task之前queue為空,顯然上述判斷為true,于是mainLoop()方法能夠繼續執行。

如果安排目前Task之前queue不為空,那麼mainLoop()方法不會一直被阻塞,不需要notify方法調用。

調用該方法還有一個好處是,如果目前安排的Task的下一次執行時間比queue中其餘Task的下一次執行時間都要小,通過notify方法可以提前打開queue.wait(executionTime - currentTime)方法對mainLoop()照成的阻塞,進而使得目前任務能夠被優先執行,有點搶占的味道。

 上述分析可以看出,Java中Timer機制的實作僅僅使用了JDK中的方法,通過wait &amp; notify機制實作,其源代碼也非常簡單,但可以想到的是這種實作機制會對開發者造成一種困擾,sched()方法中可以看出,對于一個重複執行的任務,Timer的實作機制是先安排Task下一次執行的時間,然後再啟動Task的執行,如果Task的執行時間大于下一次執行的間隔時間,可能出現不可預期的錯誤。當然,了解了Timer的實作原理,修改這種實作方式也就非常簡單了。

本文轉自demoblog部落格園部落格,原文連結http://www.cnblogs.com/0616--ataozhijia/p/3710599.html如需轉載請自行聯系原作者

demoblog