ScheduledExecutorService不像预期的那样运行

问题描述:

我试图在使用ScheduleExecutorService的固定天数之后的午夜执行任务。我的方法,我的tomcat 8的内部运行,看起来像这样:ScheduledExecutorService不像预期的那样运行

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作为specifed不执行第一次运行。例如。当我在堆栈跟踪设置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 
... 

于是莫名其妙的间隔随着时间推移变得越来越短。这里发生了什么?我的方法有问题吗?

现在当该方法第一次运行时,好像不会按照延迟和间隔的规定执行。例如。当我设置延迟= 60和间隔= 5在我的堆栈跟踪它看起来像这样...

我试图非常明确我的时间变量。在这种情况下,你应该处理毫秒,所以delayMillis,intervalMillis,aIntervalMillis等应该在你的代码中。如果它们是秒,那么使用delaySecs等等,但是当你将它们传递给期望millis的scheduleAtFixedRate(...) method时,你需要将它们倍数乘以1000。

因此,随着时间的推移,间隔在某种程度上变得越来越短。这里发生了什么?我的方法有问题吗?

可能发生的情况是,该任务试图每5毫秒计划一次,因此随机延迟只显示您的doTask()需要多长时间才能运行。如果你想分别将它们设置为60和5 那么你应该使用60000和5000来代替。

当我尝试并且我的schedule()方法正在运行时,我从eclipse(霓虹灯3)得到一条消息,说tomcat没有响应,我的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()你的线程池提交您的固定任务之后。它将继续运行,但不会提交其他工作。这是一个很好的模式。

如何以正确的方式阻止执行任务?

守护线程模式about可能是“适当”在你的情况,但你也可以取消它。 scheduler.scheduleAtFixedRate(...)方法返回一个ScheduledFuture对象,您可以调用cancel(...)来停止它。一旦你的doTask()完成它不会再安排。你也可以调用cancel(true)在线程上设置中断标志,但你的代码将不得不专门处理。

当然,您将需要检测到您的JVM正在关闭,因此您可以取消您的任务。你可以设置一个关闭钩子,这有点破解。也许有一些方法可以让tomcat通知它正在下降?

+0

感谢与线程工厂的想法,它工作正常。但计时功能仍然无法正常工作。无论我使用什么间隔时间单位,经过一段时间后,该功能变得疯狂并且越来越频繁地执行可运行的程序。 'doTask()'不应该超过2-3秒,但即使我设置了60000毫秒的时间间隔,问题仍然在发生。 – flixe