Je recherche une implémentation ExecutorService qui peut être fournie avec un délai d'expiration. Les tâches soumises à ExecutorService sont interrompues si elles prennent plus de temps que le délai d’expiration. Mettre en œuvre une telle bête n'est pas une tâche si difficile, mais je me demande si quelqu'un connaît une implémentation existante.
Voici ce que je suis venu avec une partie de la discussion ci-dessous. Des commentaires?
import java.util.List;
import java.util.concurrent.*;
public class TimeoutThreadPoolExecutor extends ThreadPoolExecutor {
private final long timeout;
private final TimeUnit timeoutUnit;
private final ScheduledExecutorService timeoutExecutor = Executors.newSingleThreadScheduledExecutor();
private final ConcurrentMap<Runnable, ScheduledFuture> runningTasks = new ConcurrentHashMap<Runnable, ScheduledFuture>();
public TimeoutThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue, long timeout, TimeUnit timeoutUnit) {
super(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue);
this.timeout = timeout;
this.timeoutUnit = timeoutUnit;
}
public TimeoutThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue, ThreadFactory threadFactory, long timeout, TimeUnit timeoutUnit) {
super(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue, threadFactory);
this.timeout = timeout;
this.timeoutUnit = timeoutUnit;
}
public TimeoutThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue, RejectedExecutionHandler handler, long timeout, TimeUnit timeoutUnit) {
super(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue, handler);
this.timeout = timeout;
this.timeoutUnit = timeoutUnit;
}
public TimeoutThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue, ThreadFactory threadFactory, RejectedExecutionHandler handler, long timeout, TimeUnit timeoutUnit) {
super(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue, threadFactory, handler);
this.timeout = timeout;
this.timeoutUnit = timeoutUnit;
}
@Override
public void shutdown() {
timeoutExecutor.shutdown();
super.shutdown();
}
@Override
public List<Runnable> shutdownNow() {
timeoutExecutor.shutdownNow();
return super.shutdownNow();
}
@Override
protected void beforeExecute(Thread t, Runnable r) {
if(timeout > 0) {
final ScheduledFuture<?> scheduled = timeoutExecutor.schedule(new TimeoutTask(t), timeout, timeoutUnit);
runningTasks.put(r, scheduled);
}
}
@Override
protected void afterExecute(Runnable r, Throwable t) {
ScheduledFuture timeoutTask = runningTasks.remove(r);
if(timeoutTask != null) {
timeoutTask.cancel(false);
}
}
class TimeoutTask implements Runnable {
private final Thread thread;
public TimeoutTask(Thread thread) {
this.thread = thread;
}
@Override
public void run() {
thread.interrupt();
}
}
}
java
multithreading
concurrency
executorservice
Edward Dale
la source
la source
protected void beforeExecute(Thread t, Runnable r)
crochet.Réponses:
Vous pouvez utiliser un ScheduledExecutorService pour cela. Tout d'abord, vous ne le soumettez qu'une seule fois pour commencer immédiatement et conserver l'avenir qui est créé. Après cela, vous pouvez soumettre une nouvelle tâche qui annulerait le futur conservé après un certain temps.
Cela exécutera votre gestionnaire (fonctionnalité principale à interrompre) pendant 10 secondes, puis annulera (c'est-à-dire interrompra) cette tâche spécifique.
la source
beforeExecute
crochet.Malheureusement, la solution est imparfaite. Il existe une sorte de bogue avec
ScheduledThreadPoolExecutor
, également signalé dans cette question : l'annulation d'une tâche soumise ne libère pas complètement les ressources mémoire associées à la tâche; les ressources ne sont libérées qu'à l'expiration de la tâche.Si vous créez par conséquent un
TimeoutThreadPoolExecutor
avec un temps d'expiration assez long (une utilisation typique) et soumettez des tâches assez rapidement, vous finissez par remplir la mémoire - même si les tâches se sont effectivement terminées avec succès.Vous pouvez voir le problème avec le programme de test (très grossier) suivant:
Le programme épuise la mémoire disponible, bien qu'il attende que les
Runnable
s générés soient terminés.J'ai réfléchi à cela pendant un moment, mais je n'ai malheureusement pas pu trouver une bonne solution.
EDIT: J'ai découvert que ce problème a été signalé comme le bogue JDK 6602600 et semble avoir été corrigé très récemment.
la source
Enveloppez la tâche dans FutureTask et vous pouvez spécifier le délai d'expiration pour FutureTask. Regardez l'exemple dans ma réponse à cette question,
Délai d'expiration du processus natif Java
la source
java.util.concurrent
classes, mais je recherche uneExecutorService
implémentation.Après une tonne de temps pour enquêter,
enfin, j'utilise la
invokeAll
méthodeExecutorService
pour résoudre ce problème.Cela interrompra strictement la tâche pendant l'exécution de la tâche.
Voici un exemple
L'avantage est que vous pouvez également soumettre
ListenableFuture
en même tempsExecutorService
.Modifiez légèrement la première ligne de code.
ListeningExecutorService
est la fonction d'écoute duExecutorService
projet at google guava ( com.google.guava ))la source
invokeAll
. Cela fonctionne très bien. Juste un mot d'avertissement pour ceux qui envisagent d'utiliser ceci: bien queinvokeAll
renvoie une liste d'Future
objets, cela semble en fait être une opération de blocage.Que diriez-vous d'utiliser la
ExecutorService.shutDownNow()
méthode décrite dans http://docs.oracle.com/javase/7/docs/api/java/util/concurrent/ExecutorService.html ? Cela semble être la solution la plus simple.la source
Il semble que le problème ne soit pas dans le bogue JDK 6602600 (il a été résolu le 22/05/2010), mais dans un appel incorrect de sleep (10) en cercle. Remarque supplémentaire, que le thread principal doit donner directement CHANCE aux autres threads pour réaliser leurs tâches en invoquant SLEEP (0) dans CHAQUE branche du cercle extérieur. Il vaut mieux, je pense, utiliser Thread.yield () au lieu de Thread.sleep (0)
La partie corrigée du résultat du code de problème précédent est comme ceci:
Il fonctionne correctement avec une quantité de compteur externe jusqu'à 150 000 000 de cercles testés.
la source
En utilisant la réponse de John W, j'ai créé une implémentation qui commence correctement le délai d'expiration lorsque la tâche commence son exécution. J'écris même un test unitaire pour ça :)
Cependant, cela ne répond pas à mes besoins car certaines opérations d'E / S ne s'interrompent pas quand
Future.cancel()
est appelé (c'est-à-dire quandThread.interrupt()
est appelé). Quelques exemples d'opérations d'E / S qui ne peuvent pas être interrompues lors de l'Thread.interrupt()
appel sontSocket.connect
etSocket.read
(et je soupçonne la plupart des opérations d'E / S implémentées dansjava.io
). Toutes les opérations d'E / S dansjava.nio
doivent être interruptibles lors de l'Thread.interrupt()
appel. Par exemple, c'est le cas pourSocketChannel.open
etSocketChannel.read
.Quoi qu'il en soit, si quelqu'un est intéressé, j'ai créé un gist pour un exécuteur de pool de threads qui permet aux tâches d'expirer (si elles utilisent des opérations interruptibles ...): https://gist.github.com/amanteaux/64c54a913c1ae34ad7b86db109cbc0bf
la source
Socket.connect
etSocket.read
myThread.interrupted()
n'est pas la bonne méthode pour interrompre, car elle EFFACE l'indicateur d'interruption. Utilisez à lamyThread.interrupt()
place, et cela devrait avec les socketsThread.interrupted()
ne permet pas d'interrompre un fil. Cependant,Thread.interrupt()
n'interrompt pas lesjava.io
opérations, il ne fonctionne que sur lesjava.nio
opérations.interrupt()
depuis de nombreuses années et cela a toujours interrompu les opérations java.io (ainsi que d'autres méthodes de blocage, telles que thread sleep, connexions jdbc, blockingqueue take, etc.). Peut-être avez-vous trouvé une classe boguée ou une JVM qui a des boguesQu'en est-il de cette idée alternative:
Un petit échantillon est ici:
la source
vérifiez si cela fonctionne pour vous,
vous pouvez restreindre le nombre d'utilisations de thread à partir du planificateur ainsi que mettre un délai d'expiration sur la tâche.
la source