Comment exécuter certaines tâches chaque jour à un moment donné en utilisant ScheduledExecutorService?

J’essaie d’exécuter une certaine tâche tous les jours à 5 heures du matin. J’ai donc décidé d’utiliser ScheduledExecutorService pour cela, mais jusqu’à présent, j’ai vu des exemples qui montrent comment exécuter une tâche toutes les quelques minutes.

Et je ne suis pas en mesure de trouver un exemple qui montre comment exécuter une tâche tous les jours à une heure donnée (5 heures du matin) le matin et aussi en tenant compte de l’heure d’été –

Vous trouverez ci-dessous mon code qui sera diffusé toutes les 15 minutes –

 public class ScheduledTaskExample { private final ScheduledExecutorService scheduler = Executors .newScheduledThreadPool(1); public void startScheduleTask() { /** * not using the taskHandle returned here, but it can be used to cancel * the task, or check if it's done (for recurring tasks, that's not * going to be very useful) */ final ScheduledFuture taskHandle = scheduler.scheduleAtFixedRate( new Runnable() { public void run() { try { getDataFromDatabase(); }catch(Exception ex) { ex.printStackTrace(); //or loggger would be better } } }, 0, 15, TimeUnit.MINUTES); } private void getDataFromDatabase() { System.out.println("getting data..."); } public static void main(Ssortingng[] args) { ScheduledTaskExample ste = new ScheduledTaskExample(); ste.startScheduleTask(); } } 

Y at-il un moyen, je peux planifier une tâche à exécuter tous les jours 5 heures du matin en utilisant ScheduledExecutorService compte tenu de l’heure d’été également?

Et aussi TimerTask est mieux pour cela ou ScheduledExecutorService ?

    Comme avec la version actuelle de java SE 8 avec son excellente API de date et heure avec java.time ce type de calcul peut être effectué plus facilement au lieu d’utiliser java.util.Calendar et java.util.Date .

    • Utilisez la classe de date et heure, par exemple LocalDateTime de cette nouvelle API
    • Utilisez la classe ZonedDateTime pour gérer le calcul spécifique au fuseau horaire, y compris les problèmes d’heure d’été. Vous trouverez un tutoriel et un exemple ici.

    Maintenant, comme exemple d’exemple pour la planification d’une tâche avec votre cas d’utilisation:

      LocalDateTime localNow = LocalDateTime.now(); ZoneId currentZone = ZoneId.of("America/Los_Angeles"); ZonedDateTime zonedNow = ZonedDateTime.of(localNow, currentZone); ZonedDateTime zonedNext5 ; zonedNext5 = zonedNow.withHour(5).withMinute(0).withSecond(0); if(zonedNow.compareTo(zonedNext5) > 0) zonedNext5 = zonedNext5.plusDays(1); Duration duration = Duration.between(zonedNow, zonedNext5); long initalDelay = duration.getSeconds(); ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(1); scheduler.scheduleAtFixedRate(new MyRunnableTask(), initalDelay, 24*60*60, TimeUnit.SECONDS); 

    L’ initalDelay est calculé pour demander au planificateur de retarder l’exécution dans TimeUnit.SECONDS . Les problèmes de différence de temps avec les millisecondes unitaires et inférieures semblent être négligeables pour ce cas d’utilisation. Mais vous pouvez toujours utiliser duration.toMillis() et TimeUnit.MILLISECONDS pour gérer les calculs de planification en millisecondes.

    Et aussi TimerTask est mieux pour cela ou ScheduledExecutorService?

    NO: ScheduledExecutorService semble mieux que TimerTask . StackOverflow a déjà une réponse pour vous .

    De @PaddyD,

    Vous avez toujours le problème: vous devez le redémarrer deux fois par an si vous souhaitez qu’il fonctionne au bon moment local. scheduleAtFixedRate ne le supprimera 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à fourni une solution de contournement (+1 pour lui), je fournis un exemple de travail avec l’API de date et heure de Java8 avec ScheduledExecutorService . Utiliser un thread de 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 de ScheduledExecutorService , utilisez toujours awaitTermination après avoir awaitTermination shutdown dessus: il y a toujours une probabilité que votre tâche soit bloquée / bloquée et que l’utilisateur attende pour toujours.

    L’exemple précédent que j’ai donné avec Calender était juste une idée que j’ai mentionnée, j’ai évité le calcul de l’heure exacte et les problèmes liés à l’heure d’été. Mise à jour de la solution par plainte de @PaddyD

    Dans Java 8:

     scheduler = Executors.newScheduledThreadPool(1); //Change here for the hour you want ----------------------------------.at() Long midnight=LocalDateTime.now().until(LocalDate.now().plusDays(1).atStartOfDay(), ChronoUnit.MINUTES); scheduler.scheduleAtFixedRate(this, midnight, 1440, TimeUnit.MINUTES); 

    Si vous n’avez pas le luxe d’utiliser Java 8, vous ferez ce dont vous avez besoin:

     public class DailyRunnerDaemon { private final Runnable dailyTask; private final int hour; private final int minute; private final int second; private final Ssortingng runThreadName; public DailyRunnerDaemon(Calendar timeOfDay, Runnable dailyTask, Ssortingng runThreadName) { this.dailyTask = dailyTask; this.hour = timeOfDay.get(Calendar.HOUR_OF_DAY); this.minute = timeOfDay.get(Calendar.MINUTE); this.second = timeOfDay.get(Calendar.SECOND); this.runThreadName = runThreadName; } public void start() { startTimer(); } private void startTimer(); { new Timer(runThreadName, true).schedule(new TimerTask() { @Override public void run() { dailyTask.run(); startTimer(); } }, getNextRunTime()); } private Date getNextRunTime() { Calendar startTime = Calendar.getInstance(); Calendar now = Calendar.getInstance(); startTime.set(Calendar.HOUR_OF_DAY, hour); startTime.set(Calendar.MINUTE, minute); startTime.set(Calendar.SECOND, second); startTime.set(Calendar.MILLISECOND, 0); if(startTime.before(now) || startTime.equals(now)) { startTime.add(Calendar.DATE, 1); } return startTime.getTime(); } } 

    Il ne nécessite aucune librairie externe et compte pour l’heure d’été. Il suffit de passer l’heure du jour où vous souhaitez exécuter la tâche en tant qu’object Calendar et la tâche en tant que Runnable . Par exemple:

     Calendar timeOfDay = Calendar.getInstance(); timeOfDay.set(Calendar.HOUR_OF_DAY, 5); timeOfDay.set(Calendar.MINUTE, 0); timeOfDay.set(Calendar.SECOND, 0); new DailyRunnerDaemon(timeOfDay, new Runnable() { @Override public void run() { try { // call whatever your daily task is here doHousekeeping(); } catch(Exception e) { logger.error("An error occurred performing daily housekeeping", e); } } }, "daily-housekeeping"); 

    NB: la tâche de la timer s’exécute dans un thread Daemon qui n’est pas recommandé pour les opérations IO. Si vous devez utiliser un fil d’utilisateur, vous devrez append une autre méthode qui annule le minuteur.

    Si vous devez utiliser un ScheduledExecutorService , modifiez simplement la méthode startTimer comme suit:

     private void startTimer() { Executors.newSingleThreadExecutor().schedule(new Runnable() { Thread.currentThread().setName(runThreadName); dailyTask.run(); startTimer(); }, getNextRunTime().getTime() - System.currentTimeMillis(), TimeUnit.MILLISECONDS); } 

    Je ne suis pas sûr du comportement, mais vous pouvez avoir besoin d’une méthode d’arrêt qui appelle shutdownNow si vous suivez l’itinéraire ScheduledExecutorService , sinon votre application risque de se bloquer lorsque vous essayez de l’arrêter.

    Avez-vous envisagé d’utiliser quelque chose comme Quartz Scheduler ? Cette bibliothèque a un mécanisme pour planifier des tâches à exécuter à une période CronScheduleBuilder chaque jour en utilisant une expression de type cron (consultez CronScheduleBuilder ).

    Un exemple de code (non testé):

     public class GetDatabaseJob implements InterruptableJob { public void execute(JobExecutionContext arg0) throws JobExecutionException { getFromDatabase(); } } public class Example { public static void main(Ssortingng[] args) { JobDetails job = JobBuilder.newJob(GetDatabaseJob.class); // Schedule to run at 5 AM every day ScheduleBuilder scheduleBuilder = CronScheduleBuilder.cronSchedule("0 0 5 * * ?"); Trigger sortinggger = TriggerBuilder.newTrigger(). withSchedule(scheduleBuilder).build(); Scheduler scheduler = StdSchedulerFactory.getDefaultScheduler(); scheduler.scheduleJob(job, sortinggger); scheduler.start(); } } 

    Il y a un peu plus de travail en amont, et vous devrez peut-être réécrire votre code d’exécution de travail, mais cela devrait vous donner plus de contrôle sur la façon dont vous voulez que votre travail s’exécute. En outre, il serait plus facile de modifier l’horaire si vous en avez besoin.

    Java8:
    Ma version de mise à jour de top answer:

    1. Correction de la situation lorsque Web Application Server ne veut pas s’arrêter, à cause du threadpool avec le thread inactif
    2. Sans récursivité
    3. Exécuter la tâche avec votre heure locale personnalisée, dans mon cas, c’est la Biélorussie, Minsk

     /** * Execute {@link AppWork} once per day. * 

    * Created by aalexeenka on 29.12.2016. */ public class OncePerDayAppWorkExecutor { private static final Logger LOG = AppLoggerFactory.getScheduleLog(OncePerDayAppWorkExecutor.class); private ScheduledExecutorService executorService = Executors.newScheduledThreadPool(1); private final Ssortingng name; private final AppWork appWork; private final int targetHour; private final int targetMin; private final int targetSec; private volatile boolean isBusy = false; private volatile ScheduledFuture scheduledTask = null; private AtomicInteger completedTasks = new AtomicInteger(0); public OncePerDayAppWorkExecutor( Ssortingng name, AppWork appWork, int targetHour, int targetMin, int targetSec ) { this.name = "Executor [" + name + "]"; this.appWork = appWork; this.targetHour = targetHour; this.targetMin = targetMin; this.targetSec = targetSec; } public void start() { scheduleNextTask(doTaskWork()); } private Runnable doTaskWork() { return () -> { LOG.info(name + " [" + completedTasks.get() + "] start: " + minskDateTime()); try { isBusy = true; appWork.doWork(); LOG.info(name + " finish work in " + minskDateTime()); } catch (Exception ex) { LOG.error(name + " throw exception in " + minskDateTime(), ex); } finally { isBusy = false; } scheduleNextTask(doTaskWork()); LOG.info(name + " [" + completedTasks.get() + "] finish: " + minskDateTime()); LOG.info(name + " completed tasks: " + completedTasks.incrementAndGet()); }; } private void scheduleNextTask(Runnable task) { LOG.info(name + " make schedule in " + minskDateTime()); long delay = computeNextDelay(targetHour, targetMin, targetSec); LOG.info(name + " has delay in " + delay); scheduledTask = executorService.schedule(task, delay, TimeUnit.SECONDS); } private static long computeNextDelay(int targetHour, int targetMin, int targetSec) { ZonedDateTime zonedNow = minskDateTime(); ZonedDateTime zonedNextTarget = zonedNow.withHour(targetHour).withMinute(targetMin).withSecond(targetSec).withNano(0); if (zonedNow.compareTo(zonedNextTarget) > 0) { zonedNextTarget = zonedNextTarget.plusDays(1); } Duration duration = Duration.between(zonedNow, zonedNextTarget); return duration.getSeconds(); } public static ZonedDateTime minskDateTime() { return ZonedDateTime.now(ZoneId.of("Europe/Minsk")); } public void stop() { LOG.info(name + " is stopping."); if (scheduledTask != null) { scheduledTask.cancel(false); } executorService.shutdown(); LOG.info(name + " stopped."); try { LOG.info(name + " awaitTermination, start: isBusy [ " + isBusy + "]"); // wait one minute to termination if busy if (isBusy) { executorService.awaitTermination(1, TimeUnit.MINUTES); } } catch (InterruptedException ex) { LOG.error(name + " awaitTermination exception", ex); } finally { LOG.info(name + " awaitTermination, finish"); } } }

    J’avais un problème similaire. Je devais planifier un tas de tâches qui devaient être exécutées pendant une journée en utilisant ScheduledExecutorService . Cela a été résolu par une tâche commençant à 3h30 du matin en programmant toutes les autres tâches par rapport à son heure actuelle . Et se reprogrammer pour le lendemain à 3h30.

    Avec ce scénario, les économies de lumière du jour ne sont plus un problème.

    Vous pouvez utiliser une simple parsing de date, si l’heure de la journée est avant maintenant, commençons demain:

      Ssortingng timeToStart = "12:17:30"; SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd 'at' HH:mm:ss"); SimpleDateFormat formatOnlyDay = new SimpleDateFormat("yyyy-MM-dd"); Date now = new Date(); Date dateToStart = format.parse(formatOnlyDay.format(now) + " at " + timeToStart); long diff = dateToStart.getTime() - now.getTime(); if (diff < 0) { // tomorrow Date tomorrow = new Date(); Calendar c = Calendar.getInstance(); c.setTime(tomorrow); c.add(Calendar.DATE, 1); tomorrow = c.getTime(); dateToStart = format.parse(formatOnlyDay.format(tomorrow) + " at " + timeToStart); diff = dateToStart.getTime() - now.getTime(); } ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(1); scheduler.scheduleAtFixedRate(new MyRunnableTask(), TimeUnit.MILLISECONDS.toSeconds(diff) , 24*60*60, TimeUnit.SECONDS); 

    Juste pour append la réponse de Victor .

    Je recommanderais d’append un chèque pour voir si la variable (dans son cas le long de midnight ) est supérieure à 1440 . Si tel est le cas, j’omettrais les .plusDays(1) , sinon la tâche ne sera exécutée que le lendemain.

    Je l’ai fait simplement comme ceci:

     Long time; final Long tempTime = LocalDateTime.now().until(LocalDate.now().plusDays(1).atTime(7, 0), ChronoUnit.MINUTES); if (tempTime > 1440) { time = LocalDateTime.now().until(LocalDate.now().atTime(7, 0), ChronoUnit.MINUTES); } else { time = tempTime; } 

    L’exemple suivant fonctionne pour moi

     public class DemoScheduler { public static void main(Ssortingng[] args) { // Create a calendar instance Calendar calendar = Calendar.getInstance(); // Set time of execution. Here, we have to run every day 4:20 PM; so, // setting all parameters. calendar.set(Calendar.HOUR, 8); calendar.set(Calendar.MINUTE, 0); calendar.set(Calendar.SECOND, 0); calendar.set(Calendar.AM_PM, Calendar.AM); Long currentTime = new Date().getTime(); // Check if current time is greater than our calendar's time. If So, // then change date to one day plus. As the time already pass for // execution. if (calendar.getTime().getTime() < currentTime) { calendar.add(Calendar.DATE, 1); } // Calendar is scheduled for future; so, it's time is higher than // current time. long startScheduler = calendar.getTime().getTime() - currentTime; // Setting stop scheduler at 4:21 PM. Over here, we are using current // calendar's object; so, date and AM_PM is not needed to set calendar.set(Calendar.HOUR, 5); calendar.set(Calendar.MINUTE, 0); calendar.set(Calendar.AM_PM, Calendar.PM); // Calculation stop scheduler long stopScheduler = calendar.getTime().getTime() - currentTime; // Executor is Runnable. The code which you want to run periodically. Runnable task = new Runnable() { @Override public void run() { System.out.println("test"); } }; // Get an instance of scheduler final ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(1); // execute scheduler at fixed time. scheduler.scheduleAtFixedRate(task, startScheduler, stopScheduler, MILLISECONDS); } } 

    référence: https://chynten.wordpress.com/2016/06/03/java-scheduler-to-run-every-day-on-specific-time/