Arrêtez une tâche périodique à partir de la tâche elle-même en cours d’exécution dans un ScheduledExecutorService

Existe-t-il un moyen intéressant d’arrêter la répétition d’une tâche à partir de la tâche elle-même lors de l’exécution de ScheduledExecutorService?

Disons que j’ai la tâche suivante:

Future f = scheduledExecutor.scheduleAtFixedRate(new Runnable() { int count = 0; public void run() { System.out.println(count++); if (count == 10) { // ??? cancel self } } }, 1, 1, TimeUnit.SECONDS); 

De l’extérieur, il est facile d’annuler via f.cancel (), mais comment puis-je arrêter la répétition à l’endroit spécifié? (Transmettre le futur via AtomicReference n’est pas sûr, car il existe une fenêtre potentielle lorsque scheduleAtFixedRate renvoie la valeur f tard et que la variable est définie en retard également, et la tâche elle-même peut déjà être exécutée, car la référence est null.)

    Lorsqu’une tâche répétée génère une exception ou une erreur, elle est placée dans le futur et la tâche n’est plus répétée. Vous pouvez lancer une exception RuntimeException ou une erreur de votre choix.

    Au lieu d’utiliser une classe interne anonyme, vous pouvez utiliser une classe nommée qui peut ensuite avoir une propriété pour l’object Future vous obtenez de l’ Executor lorsque vous planifiez une tâche.

     abstract class FutureRunnable implements Runnable { private Future future; /* Getter and Setter for future */ } 

    Lorsque vous planifiez une tâche, vous pouvez alors transmettre le Future à l’ Runnable .

     FutureRunnable runnable = new FutureRunnable() { public void run() { if (/* abort condition */) getFuture().cancel(false); } }; Future future = executor.scheduleAtFixedRate(runnable, ...); runnable.setFuture(future); 

    Peut-être devrez-vous vous assurer que la tâche n’est pas exécutée avant que l’ Future soit défini, sinon vous obtiendrez une exception NullPointerException .

    Cela semble être une mauvaise conception pour le Runnable de savoir quoi que ce soit sur l’exécuteur dans lequel il s’exécute, ou de lancer une erreur si atteindre 10 n’est pas un état d’erreur, c’est un hack.

    Pouvez-vous faire la boucle à 10 en dehors de la planification et de l’exécution? Cela peut nécessiter l’utilisation d’un exécuteur non planificateur, comme vous le feriez vous-même manuellement.

    Voici un autre moyen, qui est même sans danger pour Thread;

      final Future[] f = {null}; f[0]= scheduledExecutor.scheduleAtFixedRate(new Runnable() { int count = 0; public void run() { System.out.println(count++); if (count == 10) { Future future; while(null==(future = f[0])) Thread.yield();//prevent exceptionally bad thread scheduling future.cancel(false); return; //cancel self } } }, 1, 1, TimeUnit.SECONDS); 

    Je viens de voir ça maintenant … parce que je voulais faire la même chose … voici ma solution, je suppose que c’est du threadsafe.

    Commencez par créer un conteneur pour l’avenir:

     public static class Cancel { private ScheduledFuture future; public synchronized void setFuture(ScheduledFuture future) { this.future = future; } public synchronized void stop() { LOG.debug("cancelling {}", future); future.cancel(false); } } 

    Et puis le futur code:

      final Cancel controller = new Cancel(); synchronized (controller) { ScheduledFuture future = scheduler.scheduleWithFixedDelay(() -> { if ( 

    Notez donc que l’arrêt ne sera pas appelable tant que le futur n’a pas été créé et que le futur n’a pas été configuré sur le contrôleur.

    Gardez à l'esprit que le Runnable est la classe interne anomymique et que celle-ci sera exécutée dans un thread différent.