Java Executors: comment puis-je arrêter les tâches soumises?
-
07-07-2019 - |
Question
J'ai soumis une tâche à l'aide d'exécuteurs et j'ai besoin qu'elle s'arrête après un certain temps (par exemple 5 minutes). J'ai essayé de faire comme ça:
for (Future<?> fut : e.invokeAll(tasks, 300, TimeUnit.SECONDS)) {
try {
fut.get();
} catch (CancellationException ex) {
fut.cancel(true);
tasks.clear();
} catch(ExecutionException ex){
ex.printStackTrace(); //FIXME: gestita con printstack
}
}
Mais je reçois toujours une erreur: j'ai un vecteur partagé qui doit être modifié par les tâches, puis lu par un fil de discussion, et même si j'arrête toute la tâche, si le délai d'attente est écoulé, je reçois:
Exception in thread "Thread-1" java.util.ConcurrentModificationException
Quelque chose ne va pas? Comment puis-je arrêter les tâches soumises qui fonctionnent encore après 5 minutes?
La solution
Ce n'est pas parce que vous appelez cancel ()
sur Future
que la tâche s'arrête automatiquement. Vous devez effectuer certains travaux dans la tâche pour vous assurer qu'elle va s'arrêter:
- Utilisez
cancel (true)
pour qu'une interruption soit envoyée à la tâche. - Traitement de
InterruptedException
. Si une fonction de votre tâche génère uneInterruptedException
, veillez à quitter correctement le système dès que possible après avoir intercepté l'exception. - Vérifiez périodiquement
Thread.currentThread (). isInterrupted ()
si la tâche effectue un calcul continu.
Par exemple:
class LongTask implements Callable<Double> {
public Double call() {
// Sleep for a while; handle InterruptedException appropriately
try {
Thread.sleep(10000);
} catch (InterruptedException ex) {
System.out.println("Exiting gracefully!");
return null;
}
// Compute for a while; check Thread.isInterrupted() periodically
double sum = 0.0;
for (long i = 0; i < 10000000; i++) {
sum += 10.0
if (Thread.currentThread().isInterrupted()) {
System.out.println("Exiting gracefully");
return null;
}
}
return sum;
}
}
En outre, comme d'autres publications l'ont mentionné: ConcurrentModificationException
peut être levé même si vous utilisez la classe Vector
thread-safe, car les itérateurs que vous obtenez à partir de Vector
ne sont pas thread-safe, et doivent donc être synchronisés. La boucle for avancée utilise des itérateurs, alors faites attention:
final Vector<Double> vector = new Vector<Double>();
vector.add(1.0);
vector.add(2.0);
// Not thread safe! If another thread modifies "vector" during the loop, then
// a ConcurrentModificationException will be thrown.
for (Double num : vector) {
System.out.println(num);
}
// You can try this as a quick fix, but it might not be what you want:
synchronized (vector) { // "vector" must be final
for (Double num : vector) {
System.out.println(num);
}
}
Autres conseils
L'exception ConcurrentModificationException
provient de votre appel à tasks.clear ()
pendant que votre Exceutors parcourt vos tâches
Vector
. Ce que vous pouvez essayer de faire, c'est d'appeler shutdownNow ()
sur votre service d'exécution (
Le cas le plus courant pour ConcurrentModificationException
est lorsque le vecteur
est en cours de modification en même temps qu'il est itéré. Cela se fera souvent en un seul fil. Vous devez conserver un verrou sur le vecteur
pour l'ensemble de l'itération (en prenant soin de ne pas vous bloquer).
fut.get () est un appel bloquant. Même après l'expiration du délai, vous bloquez jusqu'à ce que la tâche soit terminée. Si vous souhaitez vous arrêter le plus près possible des 5 minutes, vous devez vérifier l'indicateur d'interruption. Je vous recommande simplement de le faire à l'aide de la méthode Thread.isInterrupted () qui préserve l'état d'interruption. Si vous souhaitez simplement vous arrêter immédiatement et n'avez pas besoin de nettoyer un état, lancez une exception qui sera interceptée par le futur et vous sera indiquée comme une ExecutionException.
fut.cancel (true) ne fait rien car la méthode invokeAll () l'a déjà fait pour vous.
Sauf si vous utilisez les " tâches " Si vous collectez quelque part ailleurs, vous n'avez probablement pas besoin d'appeler clear () dessus. Cela ne va pas être la source de votre problème puisque la méthode invokeAll () est faite avec la liste au moment où vous appelez clear (). Toutefois, si vous devez commencer à former une liste de nouvelles tâches à exécuter, je vous suggère de former une nouvelle liste de tâches et de ne pas utiliser une ancienne liste de nouvelles tâches.
Malheureusement, je n'ai pas de réponse à votre problème. Je ne vois pas assez d'informations ici pour le diagnostiquer. Rien dans l'extrait de code que vous avez fourni n'indique une utilisation incorrecte (uniquement inutile) des classes / méthodes de la bibliothèque. Peut-être que si vous incluiez une trace de pile complète au lieu de l'erreur d'une ligne.
Placez le fut.cancel (true);
dans le bloc finally