工作中常常會有定時任務的開發需求,特別是移動端。最近筆者正好有所涉及,鑒於此,結合開發中的案例說明一下幾種定時任務的退出。
需求說明:定時更新正在生成的文件大小和狀態【進行中、失敗、完成】,如果文件生成完成,則退出【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);
oldCurrentFileSize = currentFileSize; } 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) || isFinished(status) { scheduledFuture.cancel(true); return; } if (oldCurrentFileSize != currentFileSzie) { updateFileInfo(tmpPcapFileName, currentFileSize, 0);
oldCurrentFileSize = currentFileSize; } else {
updateFileInfo(tmpPacpFileName, currentFileSize, 3);
scheduleFuture.canle(true);
} } 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,那么再調度隊列排序中,會被優先調度,那么就盡量減少漏掉調度的情況。
詳情請移步:https://www.cnblogs.com/dolphin0520/p/3938991.html
*********************************************************************************
精力有限,想法太多,專注做好一件事就行
- 我只是一個程序猿。5年內把代碼寫好,技術博客字字推敲,堅持零拷貝和原創
- 寫博客的意義在於鍛煉邏輯條理性,加深對知識的系統性理解,鍛煉文筆,如果恰好又對別人有點幫助,那真是一件令人開心的事
*********************************************************************************