幾種定時任務(Timer、TimerTask、ScheduledFuture)的退出—結合真實案例【JAVA】
工作中常常會有定時任務的開發需求,特別是移動端。最近筆者正好有所涉及,鑒於此,結合開發中的案例說明一下幾種定時任務的退出。
需求說明:定時更新正在生成的文件大小和狀態【進行中、失敗、完成】,如果文件生成完成,則退出【CoderBaby】
調度可以用Timer 【調用schedule()或者scheduleAtFixedRate()方法實現】或者ScheduledExecutorService 【結合工作中其它的需求,筆者選用此】
ScheduledExecutorService的初始化(執行緒池):
private ScheduledExecutorService scheduledExecutorService = Executors.newScheduledThreadPool(3);
- 手動實現——最樸素的方案【通過sleep來控制時間間隔、break來退出】
scheduledExecutorService.execute(() -> { long oldCurFileSize = 0; while(true) { try { Thread.sleep(updateInternal * 1000); long curFileSize = Files.size(Paths.get(TMP_PCAP_PATH + tmpPcapFileName)); int status = getStatus(tmpPcapFileName); if (isFailed(status) || hasNoData(status) || isFinished(status)) { break; } if (curFileSize != oldCurFileSize) { updateFileInfo(tmpPcapFileName, curFileSize, 0); oldCurFileSize = curFileSize; } else { updateFileInfo(tmpPcapFileName, curFileSize, 3); // 延遲1秒,才能成功更新 Thread.sleep(1000); break; } } catch (Exception e) { logger.warn("Catch exception : " + e.toString()); } } });
註:
updateFileInfo—更新資料庫相關記錄;
getStatus—查詢資料庫當前記錄的狀態,判定是否完成或者出現錯誤;
updateInternal—控制定時任務的運行時間間隔(單位為秒)
- TimerTask【通過cancel來退出】
定義一個內部類繼承自TimerTask抽象類
class ScheduledUpdateTrafficForensics extends TimerTask { private String tmpPcapFileName; private long oldCurrentFileSize = 0; public ScheduledUpdateTrafficForensics(String tmpPcapFileName) { this.tmpPcapFileName = tmpPcapFileName; } public void run() { try { long currentFileSize = Files.size(Paths.get(TMP_PCAP_PATH + tmpPcapFileName)); int status = getStatus(tmpPcapFileName); if (isFailed(status) || hasNoData(status) || isFinished(status)) { this.cancel(); } if (oldCurrentFileSize != currentFileSize) { updateFileInfo(tmpPcapFileName, currentFileSize, 0); } else { updateFileInfo(tmpPcapFileName, currentFileSize, 3); this.cancel(); } } catch (IOException e) { logger.warn("Catch exception : " + e.toString()); } } }
通過scheduleAtFixedRate介面來調用(設置時間間隔和且第一次執行的延遲時間)
scheduledFuture = scheduledExecutorService.scheduleAtFixedRate(new ScheduledUpdateTrafficForensics(tmpPcapFileName), updateInternal, pcapDownloadStatusUpdateInternal, TimeUnit.SECONDS);
- ScheduledFuture【通過cancle和讀取isCancelled結果來退出】
定義一個內部類實現Runnable介面
class ScheduledUpdateTrafficForensics implements Runnable { private String tmpPcapFileName; private long oldCurrentFileSize = 0; public ScheduledUpdateTrafficForensics(String tmpPcapFileName) { this.tmpPcapFileName = tmpPcapFileName; } public void run() { while (!scheduledFuture.isCancelled()) { try { long currentFileSize = Files.size(Paths.get(TMP_PCAP_PATH + tmpPcapFileName)); int status = getStatus(tmpPcapFileName); if (isFailed(status) || hasNoData(status)) { scheduledFuture.cancel(true); return; } if (!isFinished(status)) { updateFileInfo(tmpPcapFileName, currentFileSize, 0); } } catch (IOException e) { logger.warn("Catch exception : " + e.toString()); } } } }
通過scheduleAtFixedRate介面來調用(設置時間間隔和且第一次執行的延遲時間),並且將結果返回給ScheduledFuture
scheduledFuture = scheduledExecutorService.scheduleAtFixedRate(new ScheduledUpdateTrafficForensics(tmpPcapFileName), updateInternal, pcapDownloadStatusUpdateInternal, TimeUnit.SECONDS);
註:
通過scheduledFuture.cancel(true)後可能不能成功結束定時任務,所以必須通過手動調用isCancelled()來判斷是否被cancle(調用cancel後,再調用isCancelled() 【一定會返回true】)掉了,然後退出任務。相關源碼注釋如下:
* <p>After this method returns, subsequent calls to {@link #isDone} will * always return {@code true}. Subsequent calls to {@link #isCancelled} * will always return {@code true} if this method returned {@code true}.
特別說明:
關於schedule(時間基準:運行的實際時間)和scheduleAtFixedRate(時間基準:理論時間點)的區別:
- scheduleAtFixedRate調度一個task,在delay(ms)後開始調度,然後每經過period(ms)再次調度,貌似和方法—schedule是一樣的,其實不然。
- schedule在計算下一次執行的時間的時候,是通過當前時間(在任務執行前得到) + 時間片,而scheduleAtFixedRate方法是通過當前需要執行的時間(也就是計算出現在應該執行的時間)+ 時間片,前者是運行的實際時間,而後者是理論時間點。
例如:schedule時間片是5s,那麼理論上會在5、10、15、20這些時間片被調度,但是如果由於某些CPU徵用導致未被調度,假如等到第8s才被第一次調度,那麼schedule方法計算出來的下一次時間應該是第13s而不是第10s,這樣有可能下次就越到20s後而被少調度一次或多次,而scheduleAtFixedRate方法就是每次理論計算出下一次需要調度的時間用以排序,若第8s被調度,那麼計算出應該是第10s,所以它距離當前時間是2s,那麼再調度隊列排序中,會被優先調度,那麼就盡量減少漏掉調度的情況。
詳情請移步://www.cnblogs.com/dolphin0520/p/3938991.html
*********************************************************************************
精力有限,想法太多,專註做好一件事就行
- 我只是一個程式猿。5年內把程式碼寫好,技術部落格字字推敲,堅持零拷貝和原創
- 寫部落格的意義在於鍛煉邏輯條理性,加深對知識的系統性理解,鍛煉文筆,如果恰好又對別人有點幫助,那真是一件令人開心的事
*********************************************************************************