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()函數.   回復  更多評論
            


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


          網站導航:
           
          主站蜘蛛池模板: 海安县| 化德县| 肇庆市| 河西区| 六盘水市| 昌吉市| 正阳县| 永丰县| 咸阳市| 永年县| 新巴尔虎右旗| 临漳县| 阜阳市| 长丰县| 青浦区| 高碑店市| 中阳县| 安图县| 澜沧| 大姚县| 聂拉木县| 天水市| 湾仔区| 仁布县| 阳谷县| 怀仁县| 平舆县| 库尔勒市| 太和县| 秭归县| 泾川县| 涟水县| 新郑市| 灌阳县| 河西区| 康保县| 新绛县| 台南县| 辛集市| 玉田县| 开鲁县|