Comme avec la version actuelle de java SE 8, avec son excellente API de date et d'heure, java.time
ce type de calcul peut être fait plus facilement au lieu d'utiliser java.util.Calendar
et java.util.Date
.
Maintenant, comme exemple de planification d'une tâche avec votre cas d'utilisation:
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);
Le initalDelay
est calculé pour demander au planificateur de retarder l'exécution TimeUnit.SECONDS
. Les problèmes de différence de temps avec les millisecondes unitaires et en dessous semblent être négligeables pour ce cas d'utilisation. Mais vous pouvez toujours utiliser duration.toMillis()
et TimeUnit.MILLISECONDS
gérer les calculs de planification en millisecondes.
Et aussi TimerTask est meilleur pour cela ou ScheduledExecutorService?
NON: ScheduledExecutorService
apparemment mieux que TimerTask
. StackOverflow a déjà une réponse pour vous .
De @PaddyD,
Vous rencontrez toujours le problème selon lequel vous devez le redémarrer deux fois par an si vous souhaitez qu'il s'exécute à la bonne heure locale. scheduleAtFixedRate ne le coupera que si vous êtes satisfait de la même heure UTC toute l'année.
Comme c'est vrai et que @PaddyD a déjà donné une solution de contournement (+1 pour lui), je fournis un exemple de travail avec l'API de date et heure Java8 avec ScheduledExecutorService
. Utiliser un thread démon est dangereux
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);
}
}
}
Remarque:
MyTask
est une interface avec la fonction execute
.
- Lors de l'arrêt
ScheduledExecutorService
, utilisez toujours awaitTermination
après l'invocation shutdown
: il est toujours probable que votre tâche soit bloquée / bloquée et que l'utilisateur attende indéfiniment.
L'exemple précédent que j'ai donné avec Calender n'était qu'une idée que j'ai mentionnée, j'ai évité le calcul de l'heure exacte et les problèmes d'heure d'été. Mise à jour de la solution sur la plainte de @PaddyD