[英]Why is the ScheduledExecutorService not working as expected?
[英]ScheduledExecutorService not behaving as expected
我正在嘗試使用ScheduleExecutorService
在固定天數后的午夜執行任務。 我的方法在我的 tomcat 8 內部運行,如下所示:
public void schedule (int aInterval)
{
String timezone = TimeZone.getDefault().getID();
ZoneId z = ZoneId.of(timezone);
ZonedDateTime now = ZonedDateTime.now( z );
LocalDate tomorrow = now.toLocalDate().plusDays(1);
ZonedDateTime tomorrowStart = tomorrow.atStartOfDay( z );
Duration duration = Duration.between( now , tomorrowStart );
long millisecondsUntilTomorrow = duration.toMillis();
long interval;
if (aInterval * 24 * 60 * 60 > Long.MAX_VALUE)
{
interval = Long.MAX_VALUE;
}
else
{
interval = aInterval * 24 * 60 * 60;
}
ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(1, new ThreadFactory() {
public Thread newThread(Runnable r) {
Thread thread = new Thread(r);
// allow the JVM to kill the scheduled task
thread.setDaemon(true);
return thread;
});
scheduler.scheduleAtFixedRate(new Runnable()
{
public void run() {
System.out.println(String.format("schedule::run() at %1$Td.%1$tm.%1$tY %1$tH:%1$tM:%1$tS \n", System.currentTimeMillis() ) );
doTask();
}
},
delay,
interval,
TimeUnit.SECONDS);
}
現在,當該方法第一次運行時,它似乎沒有按照delay
和interval
。 例如,當我在堆棧跟蹤中設置delay=60
和interval=5
,它看起來像這樣:
...
schedule::run() at 10.08.2017 17:57:09
schedule::run() at 10.08.2017 17:57:15
schedule::run() at 10.08.2017 17:57:21
schedule::run() at 10.08.2017 17:57:27
schedule::run() at 10.08.2017 17:57:27
schedule::run() at 10.08.2017 17:57:33
schedule::run() at 10.08.2017 17:57:33
schedule::run() at 10.08.2017 17:57:34
...
因此,隨着時間的推移,間隔不知何故變得越來越短。 這里發生了什么? 我的方法有問題嗎?
現在,當第一次運行該方法時,似乎未按延遲和間隔指定的那樣執行。 例如,當我在我的stacktrace中設置delay = 60和interval = 5時,它看起來像這樣...
我試圖對我的時間變量非常明確。 在這種情況下,您應該處理毫秒,因此delayMillis
, intervalMillis
, aIntervalMillis
等應包含在您的代碼中。 如果它們是秒,則使用delaySecs
等,但是當您將它們傳遞給期望毫秒數的scheduleAtFixedRate(...)
方法時 ,需要將它們乘以1000。
因此,間隔隨時間以某種方式變得越來越短。 這里發生了什么? 我的方法有問題嗎?
可能發生的情況是該任務試圖每5毫秒進行一次調度,因此隨機延遲只是向您顯示doTask()
運行多長時間。 如果要將它們分別設置為60和5 秒 ,則應改用60000和5000。
當我嘗試這樣做並且我的schedule()方法正在運行時,我從日食(霓虹燈3)中收到一條消息,提示tomcat沒有響應,並且tomcat無法正常關閉。
對此不確定,但我懷疑您的雄貓正在等待您計划的任務完成,而這永遠不會自己完成。 您可以使用ThreadFactory並創建一個守護程序線程,而JVM在關閉時將不等待。 請注意,如果它是守護程序線程,則可能會在運行過程中被終止。
scheduler = Executors.newScheduledThreadPool(1, new ThreadFactory() {
public Thread newThread(Runnable r) {
Thread thread = new Thread(r);
// allow the JVM to kill the scheduled task
thread.setDaemon(true);
return thread;
}
});
另外,提交固定任務后,應在線程池上調用scheduler.shutdown()
。 它將繼續運行,但是不會提交其他作業。 這是一個很好的模式。
如何以適當的方式停止執行任務?
守護程序線程模式在您的情況下可能是“正確的”,但是您也可以取消它。 scheduler.scheduleAtFixedRate(...)
方法返回一個ScheduledFuture
對象,您可以對其調用cancel(...)
來停止它。 一旦doTask()
完成,就不會再安排它了。 您還可以在其上調用cancel(true)
來在線程上設置中斷標志,但是您的代碼將必須專門處理該標志。
當然,您將需要檢測到JVM正在關閉,以便您可以取消任務。 您可以設置一個關閉鈎子,這有點麻煩。 也許tomcat有某種方法可以通知它即將崩潰?
隨着時間的推移,間隔不知何故變得越來越短
我們需要了解ScheduledExecutorService:scheduleAtFixedRate
應該如何工作。 根據文檔https://docs.oracle.com/javase/8/docs/api/java/util/concurrent/ScheduledExecutorService.html#scheduleAtFixedRate ,我們可以得出以下結論:
- 每次運行的時間都是預先確定的。
- 當同一任務的前一次運行時間比點 1 長時,這個預先計算的時間可能不符合。在這種情況下,下一次運行將在運行時間更長的前一次運行完成后立即發生。
- 同一個任務永遠不能同時運行; 即使已超過下一次運行的預期開始時間。 正如第 2 點所提到的,下一次運行將等到上一次較長的運行完成。
現在,談到您的情況,您曾期望看到您的任務以相等的時間間隔運行。 但是,每次運行它們之間的間隔反而越來越短。 這可能是由於之前的任何運行花費了相當長的時間,堆積了超過預期開始時間的其他運行。 進一步堆積的運行( doTask
方法)很快完成。 因此,所有堆積的運行都以更近的間隔運行。 如果您真的希望任務以相等的時間間隔運行,則可以改用ScheduledExecutorService: scheduleWithFixedDelay
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.