在開發中,我們經常需要一些週期性的操作,例如每隔幾分鐘就進行某一項操作。這時候我們就要去設置個定時器,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代碼
//問題一示例: 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異常,所以如果我們自定義的定時任務裡面沒有捕獲可能出現的異常而導致異常拋出後,
//問題二示例: 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線程停止,從而另後續的任務無法執行。
第三,其無法處理多個同時發生的定時任務
//問題三示例: 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啟動的時間明顯不一致代碼示例:
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是採用每線程每任務的模式工作的。
//問題一: 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運行結果,不論結果是順利完成還是有異常均會保存在這裡。
//問題二: 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
問題三由於是多線程所以我們可以保證我們的定時任務可以同時執行
//問題三: 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詳細代碼:
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定時器的發展歷程的全部內容,希望對大家有所幫助。感興趣的朋友可以繼續參閱本站:
Java實現一個簡單的定時器代碼解析
Java多線程定時器Timer原理及實現
java定時器timer的使用方法代碼示例
如有不足之處,歡迎留言指出。感謝朋友們對本站的支持!