我正在尝试使用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
...
因此,随着时间的推移,间隔会变得越来越短。这里发生了什么?我的方法有问题吗?
答案 0 :(得分:0)
现在,当首次运行该方法时,似乎没有按延迟和间隔的指定执行。例如。当我在stacktrace中设置delay = 60和interval = 5时,它看起来像这样......
我尝试对我的时间变量非常明确。在这种情况下,您应该处理毫秒,因此delayMillis
,intervalMillis
,aIntervalMillis
等应该在您的代码中。如果它们是秒,则使用delaySecs
等,但是当您将它们传递到期望毫秒的scheduleAtFixedRate(...)
method时,您需要将它们加倍。
因此,随着时间的推移,间隔会变得越来越短。这里发生了什么?我的方法有问题吗?
可能发生的是该任务正在尝试每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()
。它将继续运行,但不会提交任何其他工作。这是一个很好的模式。
如何以正确的方式停止执行任务?
守护程序线程模式在您的情况下可能是“正确的”,但您也可以取消它。 scheduler.scheduleAtFixedRate(...)
方法返回ScheduledFuture
对象,您可以调用cancel(...)
来停止它。一旦您的doTask()
完成,它将不会再次安排。你也可以在它上面调用cancel(true)
来设置线程上的中断标志,但是你的代码必须专门处理它。
当然,您需要检测到您的JVM正在关闭,以便您可以取消任务。你可以设置一个关闭钩子,这是一个黑客攻击。也许有一些方法可以让tomcat通知它正在下降?