Comment arrêter un Exécutable prévue pour l'exécution répétée après un certain nombre d'exécutions
Situation
J'ai un Exécutable. J'ai une classe qui planifie cette Praticable pour l'exécution à l'aide d'un ScheduledExecutorService avec scheduleWithFixedDelay.
Objectif
Je veux modifier cette classe à l'annexe de l'Exécutable pour le délai d'exécution soit indéfiniment, ou jusqu'à ce qu'il a été exécuté un certain nombre de fois, en fonction de certains paramètres qui sont passés dans le constructeur.
Si possible, je voudrais utiliser le même Exécutable, comme il est conceptuellement la même chose qui doit être "exécuter".
Approches possibles
Approche #1
Ont deux Runnables, qui annule le programme après un certain nombre d'exécutions (qu'il garde un comte de), et un qui ne marche pas:
public class MyClass{
private ScheduledExecutorService scheduler = Executors.newSingleThreadScheduledExecutor();
public enum Mode{
INDEFINITE, FIXED_NO_OF_TIMES
}
public MyClass(Mode mode){
if(mode == Mode.INDEFINITE){
scheduler.scheduleWithFixedDelay(new DoSomethingTask(), 0, 100, TimeUnit.MILLISECONDS);
}else if(mode == Mode.FIXED_NO_OF_TIMES){
scheduler.scheduleWithFixedDelay(new DoSomethingNTimesTask(), 0, 100, TimeUnit.MILLISECONDS);
}
}
private class DoSomethingTask implements Runnable{
@Override
public void run(){
doSomething();
}
}
private class DoSomethingNTimesTask implements Runnable{
private int count = 0;
@Override
public void run(){
doSomething();
count++;
if(count > 42){
//Cancel the scheduling.
//Can you do this inside the run method, presumably using
//the Future returned by the schedule method? Is it a good idea?
}
}
}
private void doSomething(){
//do something
}
}
Je préfère en avoir un Exécutable pour l'exécution de la méthode doSomething. Lier la planification de l'Exécutable se sent mal. Que pensez-vous de cela?
Approche #2
Ont une seule Praticable pour l'exécution du code que nous voulons exécuter périodiquement. Distincte prévue exécutable qui vérifie combien de fois le premier Exécutable a exécuter et annule quand il arrive à un certain montant. Cela peut ne pas être exacte, car il serait asynchrone. Il se sent un peu lourd. Que pensez-vous de cela?
Approche n ° 3
Étendre ScheduledExecutorService et ajouter une méthode "scheduleWithFixedDelayNTimes". Peut-être une telle catégorie existe déjà? Actuellement, je suis en utilisant Executors.newSingleThreadScheduledExecutor();
pour obtenir mon ScheduledExecutorService instance. Je aurait probablement à mettre en œuvre des fonctionnalités similaires à instancier l'étendue ScheduledExecutorService. Ce qui peut être délicat. Que pensez-vous de cela?
Aucun planificateur approche [Modifier]
Je ne pouvais pas utiliser un planificateur. Je pourrais plutôt quelque chose comme:
for(int i = 0; i < numTimesToRun; i++){
doSomething();
Thread.sleep(delay);
}
Et de l'exécuter dans un thread. Que pensez-vous de cela? Vous pourriez toujours utiliser l'exécutable, et d'appeler la méthode run directement.
Toutes les suggestions de bienvenue. Je suis à la recherche d'un débat afin de trouver la "meilleure pratique" la voie de la réalisation de mon objectif.
Vous devez vous connecter pour publier un commentaire.
Vous pouvez utiliser la méthode cancel() sur le Futur. À partir de la documentation javadoc de scheduleAtFixedRate
Voici un exemple de code qui encapsule un Exécutable dans un autre qui suit le nombre de fois où l'original a été exécuté, et annule après l'exécution de N fois.
runNTimes
méthode? Cela laisserait la planification réelle externe à l'Exécutable. Qui permettrait de planification à taux fixe / fixe délais sans avoir à ajouter un tas de méthodes. Tous les Exécutables aurait besoin serait un max de comptage et le délégué Praticable. Que pensez-vous de cela?executor.scheduleWithFixedDelay(new FixedExecutionRunnable(delegate, maxCount), 0, 100, TimeUnit.MILLISECONDS)
.runCount
besoin d'être unAtomicInteger
dans ce cas?self.cancel(false);
. Est-il possible de s'assurer querun()
sera exécuté qu'après le détachementself
?Cité de la description de l'API (
ScheduledExecutorService.scheduleWithFixedDelay
):Donc, la chose la plus simple serait de "il suffit de jeter une exception" (même si c'est considéré comme une mauvaise pratique):
++
n'est pas atomique de sorte que vous ne sont pas le bon nombre d'itérations. UtilisationAtomicInteger.getAndIncrement
à la place.Jusqu'à présent sbridges solution semble être la plus propre, à l'exception de ce que vous avez mentionné, qu'il laisse la responsabilité de gérer le nombre d'exécutions de la
Runnable
lui-même. Il ne devrait pas être concerné par le présent, plutôt que le nombre de répétitions doit être un paramètre de la classe de la manipulation de la planification. Pour atteindre cet objectif, je dirais que la conception suivants, qui introduit un nouvel exécuteur testamentaire de classe pourRunnables
. La classe fournit deux méthodes pour l'ordonnancement des tâches, qui sont la normeRunnables
, avec fini ou infini de la répétition. La mêmeRunnable
peut être passée pour fini et infini de la planification, si vous le souhaitez (ce qui n'est pas possible avec toutes les propositions de solutions qui étendent laRunnable
classe afin de fournir fini les répétitions). La manipulation de l'annulation finis répétitions est complètement encapsulé dans le planificateur de classe:Pour être juste, la logique de gestion des répétitions est toujours avec un
Runnable
, mais il a unRunnable
totalement interne à laMaxNScheduler
, alors que laRunnable
tâche a été confiée pour la planification doit pas se préoccuper de la nature de la planification. Aussi cette préoccupation peut être facilement déplacé dans le planificateur, si vous le souhaitez, par la fourniture de certaines de rappel à chaque foisRunnableWrapper.run
a été exécuté. Cela ne ferait que compliquer le code légèrement et d'introduire la nécessité de garder un peu de la carte deRunnableWrapper
s et de la répétition, qui est pourquoi j'ai opté pour garder les compteurs dans leRunnableWrapper
classe.J'ai aussi ajouté un peu de synchronisation sur l'emballage lors de la configuration de l'auto. Ceci est nécessaire que théoriquement, lors de l'exécution de la finition, de l'auto pourrait ne pas avoir encore été attribués (un assez théorique scénario, mais pour seulement 1 répétition possible).
L'annulation est géré correctement, sans jeter un
InterruptedException
et au cas où avant de l'annuler est exécuté, l'autre ronde est prévue, lesRunnableWrapper
ne vais pas appeler le sous-jacentRunnable
.Votre première approche semble OK. Vous pouvez combiner les deux types de runnables en passant par le
mode
objet de son constructeur (ou passez -1 comme le nombre maximum de fois qu'il doit exécuter), et utilisez ce mode pour déterminer si l'exécutable doit être annulé ou pas :Vous aurez à passer la date prévue de l'avenir de votre tâche pour annuler lui-même, ou vous pourriez jeter une exception.
Voici ma suggestion (je crois qu'il gère tous les cas mentionnés dans la question):
En outre, il permet à une partie externe à l'arrêt de tout, de la
Future
renvoyé par lasubmit()
méthode.Utilisation:
Pour les cas d'utilisation comme interrogation jusqu'à un certain délai d'attente, nous pouvons nous approcher avec une solution plus simple à l'aide de
Future.get()
.J'ai été à la recherche d'exactement la même fonctionnalité et choisi
org.springframework.scheduling.Trigger
.Ci-dessous est plein de test exemple (désolé si trop de flood dans le code)
applicationContext.xml
JAVA