与当前的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的抱怨更新了解决方案



