簡體   English   中英

ScheduledExecutorService 未按預期運行

[英]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);
    }

現在,當該方法第一次運行時,它似乎沒有按照delayinterval 例如,當我在堆棧跟蹤中設置delay=60interval=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時,它看起來像這樣...

我試圖對我的時間變量非常明確。 在這種情況下,您應該處理毫秒,因此delayMillisintervalMillisaIntervalMillis等應包含在您的代碼中。 如果它們是秒,則使用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. 當同一任務的前一次運行時間比點 1 長時,這個預先計算的時間可能不符合。在這種情況下,下一次運行將在運行時間更長的前一次運行完成后立即發生。
  3. 同一個任務永遠不能同時運行; 即使已超過下一次運行的預期開始時間。 正如第 2 點所提到的,下一次運行將等到上一次較長的運行完成。

現在,談到您的情況,您曾期望看到您的任務以相等的時間間隔運行。 但是,每次運行它們之間的間隔反而越來越短。 這可能是由於之前的任何運行花費了相當長的時間,堆積了超過預期開始時間的其他運行。 進一步堆積的運行( doTask方法)很快完成。 因此,所有堆積的運行都以更近的間隔運行。 如果您真的希望任務以相等的時間間隔運行,則可以改用ScheduledExecutorService: scheduleWithFixedDelay

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM