E81086713E446D36F62B2AA2A3502B5EB155

          Java雜家

          雜七雜八。。。一家之言

          BlogJava 首頁 新隨筆 聯系 聚合 管理
            40 Posts :: 1 Stories :: 174 Comments :: 0 Trackbacks
          先看個小測試程序:
          package?test;

          import?java.util.Timer;
          import?java.util.TimerTask;

          public?class?TestTimer?extends?TimerTask{

          ????
          int?number;
          ????
          /**
          ?????*?
          @param?args
          ?????
          */
          ????
          public?static?void?main(String[]?args)?{
          ????????Timer?timer
          =new?Timer();
          ????????timer.schedule(
          new?TestTimer(), 1000, 1000);
          ????????
          ????}

          ????
          public?void?run()?{
          ???????System.out.println(
          "number="+(++number));
          ????????
          ????}

          }
          正常情況該程序每秒打印當前的 number值。
          現在我們來改變這個正常情況:
          1)保持程序運行
          2)通過系統時間欄把系統時間往前調一天。

          過一會兒,你會發現該程序停止輸出了?

          對,要看到下一個輸出,你得等一天了,在這種情況下,你即使現在重新調整到正確系統時間,你仍然得等到下一天才能看到輸出。

          為什么呢?
          下面是JDK中Timer調度的核心代碼(在原有注釋的基礎上加了一些):
          ?/**
          ?????*?The?main?timer?loop.??(See?class?comment.)
          ?????
          */
          ????
          private?void?mainLoop()?{
          ????????
          while?(true)?{
          ????????????
          try?{
          ????????????????
          //to?be?scheduled?task
          ????????????????TimerTask?task;
          ????????????????
          //fired?one?task?
          ????????????????boolean?taskFired;
          ????????????????
          synchronized(queue)?{
          ????????????????????
          //?Wait?for?queue?to?become?non-empty
          ????????????????????while?(queue.isEmpty()?&&?newTasksMayBeScheduled)
          ????????????????????????queue.wait();
          ????????????????????
          if?(queue.isEmpty())
          ????????????????????????
          break;?//?Queue?is?empty?and?will?forever?remain;?die

          ????????????????????
          //?Queue?nonempty;?look?at?first?evt?and?do?the?right?thing
          ????????????????????long?currentTime,?executionTime;
          ????????????????????task?
          =?queue.getMin();
          ????????????????????
          synchronized(task.lock)?{
          ????????????????????????
          if?(task.state?==?TimerTask.CANCELLED)?{
          ????????????????????????????
          //find?the?first?task?from?the?queue.
          ????????????????????????????queue.removeMin();
          ????????????????????????????
          continue;??//?No?action?required,?poll?queue?again
          ????????????????????????}
          ????????????????????????
          //here?,use?system?current?time?to?determin?whether?or?not?fire?a?task
          ????????????????????????currentTime?=?System.currentTimeMillis();
          ????????????????????????executionTime?
          =?task.nextExecutionTime;
          ????????????????????????
          //So?when?we?change?system?time?to?long?ago,this?expression?will?be?evaluate?to?false
          ????????????????????????if?(taskFired?=?(executionTime<=currentTime))?{
          ????????????????????????????
          if?(task.period?==?0)?{?//?Non-repeating,?remove
          ????????????????????????????????queue.removeMin();
          ????????????????????????????????task.state?
          =?TimerTask.EXECUTED;
          ????????????????????????????}?
          else?{?//?Repeating?task,?reschedule
          ????????????????????????????????queue.rescheduleMin(
          ??????????????????????????????????task.period
          <0???currentTime???-?task.period
          ????????????????????????????????????????????????:?executionTime?
          +?task.period);
          ????????????????????????????}
          ????????????????????????}
          ????????????????????}
          ????????????????????
          if?(!taskFired)?//?Task?hasn't?yet?fired;?wait
          ????????????????????????queue.wait(executionTime?-?currentTime);
          ????????????????}
          ????????????????
          if?(taskFired)??//?Task?fired;?run?it,?holding?no?locks
          ????????????????????task.run();
          ????????????}?
          catch(InterruptedException?e)?{
          ????????????}
          ????????}
          ????}
          那么,我們如何能讓Timer調度不依賴這種系統時間呢?
          Windows API中GetTickCount()返回系統自啟動以來的毫秒數。我們可以用這個來替代System.currentTimeMillis().這樣即使改變系統時間
          Timer的調度也不會所影響。

          改完后再來測試一把
          public?class?TestTimer2?extends?com.yovn.labs.util.TimerTask{
          ????
          int?number;
          ????
          /**
          ?????*?
          @param?args
          ?????
          */
          ????
          public?static?void?main(String[]?args)?{
          ????????com.yovn.labs.util.Timer?timer
          =new?com.yovn.labs.util.Timer();
          ????????timer.schedule(
          new?TestTimer2(),?1000,?1000);
          ????????
          ????}

          ????
          public?void?run()?{
          ???????System.out.println(
          "number="+(++number));
          ????????
          ????}

          }

          這時的輸出沒有再受系統時間的改變了。

          本文代碼下載:
          source code

          posted on 2007-03-17 10:40 DoubleH 閱讀(2188) 評論(3)  編輯  收藏

          Feedback

          # re: Fix JDK Timer的bug(確認是個bug,見http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=4290274) 2007-03-17 12:45 BeanSoft
          看了看那個 Bug 頁, 貌似 JDK 1.5 也沒 Fix. Submit Date 11-NOV-1999. 所以我覺得要 Fix 就必須每個操作系統都提供系統啟動時鐘的功能, 但是你的這個就會引入另一個 Bug, 如果系統重啟, 然后啟動時鐘就會被重置(我們假設應用存儲狀態后繼續執行), 依賴這種做法的定時器也會失效. 所以我覺得 Sun 沒有按照這種想法來 Fix 第一是不是所有系統都提供系統啟動時間(典型的嵌入式設備)的API, 第二是這種做法又會引入新的問題. 一般來說依賴當前時間做定時是個比較簡單實用的思路, Windows 軟件防止過期后繼續使用也是在注冊表里記錄第一次使用的時間, 以后如果發現時間超前就會判定為超出試用期.

          JDK 必須要為不同的平臺提供一致的特性, 所以這個 Bug 改起來還真是有點困難. 不過添加一個能獲取系統啟動時間的 API 倒是值得考慮(部分平臺可用).

          發現剛發的評論不小心改掉了, 我說的是服務器在生產環境下不中斷應用這么大的時間改動很危險, 不現實.  回復  更多評論
            

          # re: Fix JDK Timer的bug(確認是個bug,見http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=4290274) 2007-03-17 13:04 Javacap
          @BeanSoft
          用戶可能誤操作,改完以后再改回來也是不行的。另外也不一定要這么大的時間改變。改一分鐘,就會導致延遲一分鐘。假如用戶使用scheduleAtFixRate,則本來期望一段時間內執行N次,而實際上少執行了幾次,這是不可接受的。

          在Linux下,GetTickCount()可以用times()函數來實現。
          #include <sys/times.h>

          long GetTickCount()
          {
          struct tms tm;
          return times(&tm);
          }

            回復  更多評論
            

          # re: Fix JDK Timer的bug(http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=4290274) 2007-03-17 18:38 喜來樂哈哈
          選擇SWT的話,用Display.timerExec (int milliseconds, Runnable runnable)就不會有這個問題. 它是調用系統原生的timer功能,Windows下SetTimer()函數, gtk下用gtk_timeout_add()函數.   回復  更多評論
            


          只有注冊用戶登錄后才能發表評論。


          網站導航:
           
          主站蜘蛛池模板: 吐鲁番市| 兴化市| 大悟县| 新津县| 兰考县| 永春县| 长乐市| 梧州市| 开原市| 监利县| 彩票| 五原县| 钟祥市| 双峰县| 祥云县| 报价| 迁西县| 湘西| 古浪县| 丁青县| 什邡市| 拜泉县| 讷河市| 旬阳县| 石屏县| 商城县| 普陀区| 南部县| 双鸭山市| 汉中市| 平顶山市| 碌曲县| 利川市| 成安县| 重庆市| 玛多县| 宁武县| 桦甸市| 柳江县| 茶陵县| 贵州省|