小编典典
与当前的Java SE 8版本一样,它具有出色的日期时间API,与java.time使用java.util.Calendarand相比,可以更轻松地完成这些计算 java.util.Date。
使用此新API的日期时间类,即LocalDateTime
使用ZonedDateTime类处理时区特定的计算,包括夏令时问题。你将在此处找到教程和示例。
现在作为使用你的用例计划任务的示例示例:
ZonedDateTime now = ZonedDateTime.now(ZoneId.of("America/Los_Angeles"));
ZonedDateTime nextRun = now.withHour(5).withMinute(0).withSecond(0);
if(now.compareTo(nextRun) > 0)
nextRun = nextRun.plusDays(1);
Duration duration = Duration.between(now, nextRun);
long initalDelay = duration.getSeconds();
ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(1);
scheduler.scheduleAtFixedRate(new MyRunnableTask(),
initalDelay,
TimeUnit.DAYS.toSeconds(1),
TimeUnit.SECONDS);
该initalDelay计算问调度延迟在执行TimeUnit.SECONDS。对于此用例,单位毫秒及以下的时差问题似乎可以忽略不计。但是你还是可以使用的duration.toMillis(),并TimeUnit.MILLISECONDS以毫秒为单位处理调度computaions。
而且TimerTask对此或ScheduledExecutorService更好吗?
NO: ScheduledExecutorService似乎比更好TimerTask。StackOverflow已经为你提供了答案。
从@PaddyD,
你仍然遇到问题,如果希望它在正确的本地时间运行,则需要每年重新启动两次。scheduleAtFixedRate不会削减它,除非你对全年相同的UTC时间感到满意。
确实如此,@ PaddyD已经给出了解决方法(给他+1),我提供了一个Java8日期时间API和的工作示例ScheduledExecutorService。使用守护程序线程很危险
class MyTaskExecutor
{
ScheduledExecutorService executorService = Executors.newScheduledThreadPool(1);
MyTask myTask;
volatile boolean isStopIssued;
public MyTaskExecutor(MyTask myTask$)
{
myTask = myTask$;
}
public void startExecutionAt(int targetHour, int targetMin, int targetSec)
{
Runnable taskWrapper = new Runnable(){
@Override
public void run()
{
myTask.execute();
startExecutionAt(targetHour, targetMin, targetSec);
}
};
long delay = computeNextDelay(targetHour, targetMin, targetSec);
executorService.schedule(taskWrapper, delay, TimeUnit.SECONDS);
}
private long computeNextDelay(int targetHour, int targetMin, int targetSec)
{
LocalDateTime localNow = LocalDateTime.now();
ZoneId currentZone = ZoneId.systemDefault();
ZonedDateTime zonedNow = ZonedDateTime.of(localNow, currentZone);
ZonedDateTime zonedNextTarget = zonedNow.withHour(targetHour).withMinute(targetMin).withSecond(targetSec);
if(zonedNow.compareTo(zonedNextTarget) > 0)
zonedNextTarget = zonedNextTarget.plusDays(1);
Duration duration = Duration.between(zonedNow, zonedNextTarget);
return duration.getSeconds();
}
public void stop()
{
executorService.shutdown();
try {
executorService.awaitTermination(1, TimeUnit.DAYS);
} catch (InterruptedException ex) {
Logger.getLogger(MyTaskExecutor.class.getName()).log(Level.SEVERE, null, ex);
}
}
}
注意:
MyTask是具有功能的接口execute。
在停止时ScheduledExecutorService,请始终awaitTermination在调用后使用shutdown:始终有可能你的任务被卡死/死锁,并且用户将永远等待。
我给Calender给出的上一个示例只是我提到的一个想法,避免了精确的时间计算和夏时制问题。根据@PaddyD的抱怨更新了解决方案
2020-03-18