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);
}
现在,当该方法好像通过delay
和interval
作为specifed不执行第一次运行。例如。当我在堆栈跟踪设置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
...
于是莫名其妙的间隔随着时间推移变得越来越短。这里发生了什么?我的方法有问题吗?
现在当该方法第一次运行时,好像不会按照延迟和间隔的规定执行。例如。当我设置延迟= 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通知它正在下降?
感谢与线程工厂的想法,它工作正常。但计时功能仍然无法正常工作。无论我使用什么间隔时间单位,经过一段时间后,该功能变得疯狂并且越来越频繁地执行可运行的程序。 'doTask()'不应该超过2-3秒,但即使我设置了60000毫秒的时间间隔,问题仍然在发生。 – flixe