Come con l'attuale versione di Java SE 8 con la sua eccellente API di data e ora, java.time
questo tipo di calcolo può essere fatto più facilmente invece di usare java.util.Calendar
e java.util.Date
.
Ora come esempio di esempio per la pianificazione di un'attività con il tuo caso d'uso:
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);
La initalDelay
viene calcolata a chiedere lo scheduler di ritardare l'esecuzione in TimeUnit.SECONDS
. I problemi di differenza di fuso orario con unità millisecondi e inferiori sembrano essere trascurabili per questo caso d'uso. È comunque possibile utilizzare duration.toMillis()
e TimeUnit.MILLISECONDS
per gestire i calcoli di pianificazione in millisecondi.
E anche TimerTask è meglio per questo o ScheduledExecutorService?
NO: ScheduledExecutorService
apparentemente migliore di TimerTask
. StackOverflow ha già una risposta per te .
Da @PaddyD,
Hai ancora il problema per cui devi riavviare questo due volte l'anno se vuoi che funzioni all'ora locale giusta. scheduleAtFixedRate non lo taglierà a meno che tu non sia soddisfatto della stessa ora UTC per tutto l'anno.
Poiché è vero e @PaddyD ha già fornito una soluzione alternativa (+1 a lui), sto fornendo un esempio funzionante con l'API di data e ora Java8 con ScheduledExecutorService
. L'uso del thread daemon è pericoloso
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);
}
}
}
Nota:
MyTask
è un'interfaccia con funzioni execute
.
- Durante l'arresto
ScheduledExecutorService
, usa sempre awaitTermination
dopo averlo richiamato shutdown
: c'è sempre una probabilità che la tua attività sia bloccata / in fase di stallo e l'utente aspetterà per sempre.
L'esempio precedente che ho fornito con Calender era solo un'idea che ho menzionato, ho evitato il calcolo esatto dell'ora e problemi di risparmio legale. Aggiornata la soluzione in base al reclamo di @PaddyD