在開發中,我們經常需要一些周期性的操作,例如每隔幾分鐘就進行某一項操作。這時候我們就要去設置個定時器,Java中最方便、最高效的實現方式是用java.util.Timer工具類,再通過調度java.util.TimerTask任務。
Timer是一種工具,線程用其安排以后在后臺線程中執行的任務。可安排任務執行一次,或者定期重復執行。實際上是個線程,定時調度所擁有的TimerTasks。
TimerTask是一個抽象類,它的子類由Timer安排為一次執行或重復執行的任務。實際上就是一個擁有run方法的類,需要定時執行的代碼放到run方法體內。
java在jdk1.3中推出了定時器類Timer,而后在jdk1.5后由DouLea從新開發出了支持多線程的ScheduleThreadPoolExecutor,從后者的表現來看,可以考慮完全替代Timer了。
Timer與ScheduleThreadPoolExecutor對比:
1.Timer始于jdk1.3,其原理是利用一個TimerTask數組當作隊列,將所有定時任務添加到此隊列里面去。然后啟動一個線程,當隊列為空時,此線程會阻塞,當隊列里面有數據時,線程會去除一個TimerTask來判斷
是否到時間需要運行此任務,如果運行時間小于或等于當前時間時則開始運行任務。由于其單線程的本質,所以會帶來幾個問題(詳細代碼在后面):
第一,當我們添加到定時器中的任務比較耗時時,由于此定時器是單線程順序執行定時器任務,所以會影響后續任務的按時執行。
Java代碼
1
2
3
4
5
6
7
8
9
10
11
12
13
|
//問題一示例: m_timer.scheduleAtFixedRate( new TaskUseLongTime(), 1000 , 5000 ); m_timer.scheduleAtFixedRate( new TaskNormal(), 5000 , 3000 ); 運行結果: 14 : 44 : 29 : timer is sleeping 10 seconds 14 : 44 : 39 : Task Normal executed 14 : 44 : 39 : timer is sleeping 10 seconds 14 : 44 : 49 : Task Normal executed 14 : 44 : 49 : Task Normal executed 14 : 44 : 49 : timer is sleeping 10 seconds 結果分析:TaskNormal任務無法保證 3 秒運行一次,其只能等待TaskUseLongTime運行結束后才可以。 |
第二,Timer中的線程僅僅會捕獲InterruptedException異常,所以如果我們自定義的定時任務里面沒有捕獲可能出現的異常而導致異常拋出后,
1
2
3
4
5
6
7
8
9
10
11
12
13
|
//問題二示例: m_timer.schedule( new TaskThrowException(), 1000 ); m_timer.schedule( new TaskNormal(), 2000 ); 運行結果: 14 : 47 : 37 : Throw exception Exception in thread "Timer-0" java.lang.RuntimeException at timer_test.TimerTest$TaskThrowException.run(TimerTest.java: 85 ) at java.util.TimerThread.mainLoop(Timer.java: 512 ) at java.util.TimerThread.run(Timer.java: 462 ) 結果分析: 當前一個任務拋出異常后,后面的TaskNormal任務無法繼續運行 |
會導致我們的Timer線程停止,從而另后續的任務無法執行。
第三,其無法處理多個同時發生的定時任務
1
2
3
4
5
6
7
8
9
10
11
|
//問題三示例: m_timer.scheduleAtFixedRate( new TaskUseLongTime( "timer1" ), 1000 , 15000 ); m_timer.scheduleAtFixedRate( new TaskUseLongTime( "timer2" ), 1000 , 15000 ); 運行結果: 14 : 50 : 16 : timer1 is sleeping 10 seconds 14 : 50 : 26 : timer2 is sleeping 10 seconds 14 : 50 : 36 : timer2 is sleeping 10 seconds 結果分析: 我的啟動時間均是 1 秒以后,但是timer1和timer2啟動的時間明顯不一致 |
代碼示例:
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
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
|
package timer_test; import java.text.SimpleDateFormat; import java.util.Date; import java.util.Timer; import java.util.TimerTask; public class TimerTest { private final Timer m_timer = new Timer(); public static void main(String[] args) { new TimerTest().test(); } public void test() { //問題一示例: m_timer.scheduleAtFixedRate( new TaskUseLongTime(), 1000 , 5000 ); m_timer.scheduleAtFixedRate( new TaskNormal(), 5000 , 3000 ); //問題二示例: // m_timer.schedule(new TaskThrowException(), 1000); // m_timer.schedule(new TaskNormal(), 2000); //問題三示例: // m_timer.scheduleAtFixedRate(new TaskUseLongTime("timer1"), 1000, 5000); // m_timer.scheduleAtFixedRate(new TaskUseLongTime("timer2"), 1000, 5000); } private class TaskUseLongTime extends TimerTask { private String m_taskName = "timer" ; public TaskUseLongTime(){ } public TaskUseLongTime(String taskName) { m_taskName = taskName; } @Override public void run() { try { System.out.println(getCurrentTime()+ ": " +m_taskName+ " is sleeping 10 seconds" ); Thread.sleep( 10000 ); } catch (InterruptedException e) { } } } private class TaskNormal extends TimerTask { @Override public void run() { System.out.println(getCurrentTime()+ ": Task Normal executed" ); } } private class TaskThrowException extends TimerTask { @Override public void run() { System.out.println(getCurrentTime()+ ": Throw exception" ); throw new RuntimeException(); } } private String getCurrentTime() { return new SimpleDateFormat( "HH:mm:ss" ).format( new Date()); } } |
2.ScheduleThreadPoolExecutor
ScheduleThreadPoolExecutor始于jdk1.5,是由DouLea先生編寫的,其利用ThreadPoolExecutor和DelayQueue巧妙的結合完成了多線程定時器的實現,解決了Timer中由于單線程而導致的上述三個缺陷。
問題一中的問題是因為單線程順序執行導致后續任務無法按時完成,我們看到多線程可以很容易的解決此問題,同時我們注意到TaskUseLongTime的執行時間為10s(請看后續代碼),我們定時任務間隔是5秒,但是從結果中發現我們的任務執行間隔卻是10秒,所以我們可以判斷ScheduleThreadPoolExecutor是采用每線程每任務的模式工作的。
1
2
3
4
5
6
7
8
9
10
11
|
//問題一: m_timer.scheduleAtFixedRate( new TaskUseLongTime(), 1000 , 5000 , TimeUnit.MILLISECONDS); m_timer.scheduleAtFixedRate( new TaskNormal(), 1000 , 5000 , TimeUnit.MILLISECONDS); 運行結果: 14 : 54 : 37 : Task Normal executed 14 : 54 : 37 : timer is sleeping 10 seconds 14 : 54 : 42 : Task Normal executed 14 : 54 : 47 : Task Normal executed 14 : 54 : 47 : timer is sleeping 10 seconds 14 : 54 : 52 : Task Normal executed |
問題二中我們發現當拋出異常的任務執行后不影響其他任務的運行,同時我們發現在運行結果里面沒有將我們的異常拋出,這是因為ScheduleThreadPoolExecutor類在執行完定時任務后會返回一個ScheduledFuture運行結果,不論結果是順利完成還是有異常均會保存在這里。
1
2
3
4
5
6
7
8
9
10
11
|
//問題二: m_timer.scheduleAtFixedRate( new TaskThrowException(), 1000 , 5000 , TimeUnit.MILLISECONDS); m_timer.scheduleAtFixedRate( new TaskNormal(), 1000 , 5000 , TimeUnit.MILLISECONDS); 運行結果: 14 : 58 : 36 : Throw exception 14 : 58 : 36 : Task Normal executed 14 : 58 : 41 : Task Normal executed 14 : 58 : 46 : Task Normal executed 14 : 58 : 51 : Task Normal executed 14 : 58 : 56 : Task Normal executed |
問題三由于是多線程所以我們可以保證我們的定時任務可以同時執行
1
2
3
4
5
6
7
8
9
10
11
|
//問題三: m_timer.scheduleAtFixedRate( new TaskUseLongTime( "timer1" ), 1000 , 5000 , TimeUnit.MILLISECONDS); m_timer.scheduleAtFixedRate( new TaskUseLongTime( "timer2" ), 1000 , 5000 , TimeUnit.MILLISECONDS); 運行結果: 15 : 01 : 12 : timer1 is sleeping 10 seconds 15 : 01 : 12 : timer2 is sleeping 10 seconds 15 : 01 : 22 : timer2 is sleeping 10 seconds 15 : 01 : 22 : timer1 is sleeping 10 seconds 15 : 01 : 32 : timer1 is sleeping 10 seconds 15 : 01 : 32 : timer2 is sleeping 10 seconds |
詳細代碼:
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
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
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
|
package timer_test; import java.text.SimpleDateFormat; import java.util.Date; import java.util.concurrent.Callable; import java.util.concurrent.ScheduledThreadPoolExecutor; import java.util.concurrent.TimeUnit; public class ScheduleThreadPoolExecutorTest { private final ScheduledThreadPoolExecutor m_timer = new ScheduledThreadPoolExecutor( 10 ); public static void main(String[] args) { ScheduleThreadPoolExecutorTest timerTest = new ScheduleThreadPoolExecutorTest(); timerTest.test(); try { Thread.sleep( 100000 ); } catch (InterruptedException e) { } finally { timerTest.shutdown(); } } public void shutdown() { m_timer.shutdown(); } public void test() { //問題一: // m_timer.scheduleAtFixedRate(new TaskUseLongTime(), 1000, 5000, TimeUnit.MILLISECONDS); // m_timer.scheduleAtFixedRate(new TaskNormal(), 1000, 5000, TimeUnit.MILLISECONDS); //問題二: // m_timer.scheduleAtFixedRate(new TaskThrowException(), 1000, 5000, TimeUnit.MILLISECONDS); // m_timer.scheduleAtFixedRate(new TaskNormal(), 1000, 5000, TimeUnit.MILLISECONDS); //問題三: m_timer.scheduleAtFixedRate( new TaskUseLongTime( "timer1" ), 1000 , 5000 , TimeUnit.MILLISECONDS); m_timer.scheduleAtFixedRate( new TaskUseLongTime( "timer2" ), 1000 , 5000 , TimeUnit.MILLISECONDS); } private class TaskUseLongTime implements Callable<Integer>, Runnable { private String m_taskName = "timer" ; private TaskUseLongTime(){ } private TaskUseLongTime(String taskName) { m_taskName = taskName; } public void run() { try { System.out.println(getCurrentTime()+ ": " +m_taskName+ " is sleeping 10 seconds" ); Thread.sleep( 10000 ); } catch (InterruptedException e) { } } public Integer call() throws Exception { run(); return 0 ; } } @SuppressWarnings ( "unused" ) private class TaskNormal implements Callable<Integer>, Runnable { public Integer call() throws Exception { run(); return 0 ; } public void run() { System.out.println(getCurrentTime()+ ": Task Normal executed" ); } } @SuppressWarnings ( "unused" ) private class TaskThrowException implements Callable<Integer>, Runnable { public Integer call() throws Exception { System.out.println(getCurrentTime()+ ": Throw exception" ); throw new RuntimeException(); } public void run() { System.out.println(getCurrentTime()+ ": Throw exception" ); throw new RuntimeException(); } } private String getCurrentTime() { return new SimpleDateFormat( "HH:mm:ss" ).format( new Date()); } } |
總結
以上就是本文關于淺談java定時器的發展歷程的全部內容,希望對大家有所幫助。如有不足之處,歡迎留言指出。感謝朋友們對本站的支持!
原文鏈接:http://hacksin.iteye.com/blog/2157572