J'ai un projet multi-thread Java et je veux ajouter une méthode stop () pour arrêter tous les threads en cours d'exécution. Le problème est que ce projet est développé par quelqu'un d'autre, et je ne suis pas familiarisé avec la façon dont il implémente plusieurs threads.
Ce que je sais, c'est qu'une fois le projet démarré, de nombreux threads sont invoqués et s'exécutent indéfiniment. Existe-t-il un moyen de trouver tous les threads en cours d'exécution et de les arrêter? J'ai beaucoup cherché et trouvé comment obtenir une liste des threads en cours d'exécution:
Set<Thread> threadSet = Thread.getAllStackTraces().keySet();
Que faire ensuite pour arrêter tous les threads en cours d'exécution?
La raison pour laquelle je souhaite arrêter ces threads est que je dois déployer ce projet sur le conteneur OSGi en tant que bundle. Une fois le bundle démarré, plusieurs threads s'exécutent indéfiniment. J'ai donc besoin d'implémenter une méthode destroy () pour arrêter tous les threads afin de contrôler le cycle de vie du bundle.
Que diriez-vous
for (Thread t : Thread.getAllStackTraces().keySet())
{ if (t.getState()==Thread.State.RUNNABLE)
t.interrupt();
}
for (Thread t : Thread.getAllStackTraces().keySet())
{ if (t.getState()==Thread.State.RUNNABLE)
t.stop();
}
C'est une idée dangereuse. Le Javadoc pour Thread.stop()
explique:
Cette méthode est intrinsèquement dangereuse. L'arrêt d'un thread avec Thread.stop provoque le déverrouillage de tous les moniteurs qu'il a verrouillés (comme conséquence naturelle de l'exception ThreadDeath non vérifiée se propageant dans la pile). Si l'un des objets précédemment protégés par ces moniteurs était dans un état incohérent, les objets endommagés deviennent visibles pour les autres threads, ce qui peut entraîner un comportement arbitraire. De nombreuses utilisations de stop doivent être remplacées par du code qui modifie simplement une variable pour indiquer que le thread cible doit cesser de fonctionner. Le thread cible doit vérifier cette variable régulièrement et revenir de sa méthode d'exécution de manière ordonnée si la variable indique qu'elle doit s'arrêter. Si le thread cible attend pendant de longues périodes (sur une variable de condition, par exemple), la méthode d'interruption doit être utilisée pour interrompre l'attente.
Fondamentalement, les threads doivent être construits et conçus pour se terminer en toute sécurité, il n'est pas possible de tuer en toute sécurité des threads arbitraires. Un modèle assez standard est implémenté comme suit:
public abstract class StoppableRunnable implements Runnable {
private volatile boolean stopWork;;
private boolean done;
public final void run() {
setup();
while(!stopWork && !done) {
doUnitOfWork();
}
cleanup();
}
/**
* Safely instructs this thread to stop working,
* letting it finish it's current unit of work,
* then doing any necessary cleanup and terminating
* the thread. Notice that this does not guarentee
* the thread will stop, as doUnitOfWork() could
* block if not properly implemented.
*/
public void stop() {
stopWork = true;
}
protected void done() {
done = true;
}
protected void setup() { }
protected void cleanup() { }
/**
* Does as small a unit of work as can be defined
* for this thread. Once there is no more work to
* be done, done() should be called.
*/
protected abstract void doUnitOfWork();
}
Vous avez laissé entendre que vous n'êtes pas l'auteur de ces discussions, ce qui suggère qu'elles ne peuvent pas être arrêtées en toute sécurité. Dans un tel cas, vous pouvez appeler Thread.interrupt()
pour demander au thread d'arrêter ce qu'il fait (au lieu du modèle décrit ci-dessus, vous pouvez utiliser Thread.interrupt()
pour effet similaire), mais de la même manière, si le concepteur du thread ne l'a pas écrit pour gérer les interruptions, cela peut ne rien faire ou provoquer des états incohérents ou d'autres erreurs.
En fin de compte, Thread.stop()
est ce que vous voulez si vous voulez simplement "[Forcer] le thread à arrêter l'exécution" et ne pouvez pas modifier l'implémentation du thread; cependant, comme utiliser kill
sous Unix, c'est une proposition dangereuse, et vous devez essentiellement considérer que votre JVM est dans un état instable et irréparable après avoir terminé un thread de cette manière, et essayez de quitter le programme aussi rapidement que possible par la suite.
Concernant votre suggestion d'interrompre puis d'arrêter:
Il y a encore beaucoup de problèmes ici, en particulier, l'interruption ne garantit pas que le thread interrompra immédiatement (cela fonctionne de manière similaire, mais moins explicite, à mon StoppableRunnable
ci-dessus) et définit à la place un indicateur que le thread doit interrompre lorsque possible. Cela signifie que vous pouvez appeler Thread.interrupt()
, le thread peut démarrer son comportement de gestion des interruptions approprié, puis à mi-chemin, votre appel à Thread.stop()
se déclenche, tuant violemment le thread et potentiellement cassant votre JVM . Les appels à Thread.interrupt()
ne fournissent aucune garantie quant au moment ou à la façon dont le thread répondra à cette interruption, c'est pourquoi je préfère le comportement explicite dans StoppableRunnable
. Inutile de dire que si vous voulez appeler Thread.stop()
, il y a peu à gagner en appelant Thread.interrupt()
en premier. Je ne le recommande pas, mais vous pourriez tout aussi bien appeler simplement Thread.stop()
en premier lieu.
De plus, sachez que le code exécutant votre boucle est lui-même dans un thread - ce qui signifie que votre boucle pourrait très bien se tuer en premier, laissant tous les autres threads en cours d'exécution.
J'ai utilisé ce code:
package com.xxx;
import com.xxx.tools.messageLog;
import Java.util.logging.Level;
import Java.util.logging.Logger;
/**
*
* @author Mostafa Nazari
*/
public class ProcessAgent extends Thread{
public ProcessAgent() {
super("Process-Agent");
}
@Override
public void run() {
try {
com.xxx.Process.agent.Main.main(new String[]{});
} catch (Exception ex) {
if (ex instanceof InterruptedException) {
messageLog.stdwar("Process Agent Stopped");
}else {
messageLog.stderr("FATAL Error: "+ex.getMessage());
Logger.getLogger(ProcessAgent.class.getName()).log(Level.SEVERE, null, ex);
}
}
}
public void terminate() {
if (super.isAlive()) {
ThreadGroup group = super.getThreadGroup();
while (group != null) {
group .interrupt();
group = super.getThreadGroup();
}
super.interrupt();
}
}
public String getStatus() {
if (super.isAlive()) {
return "running";
} else {
if (super.isInterrupted())
return "stopping";
else
return "stopped";
}
}
}
espérons qu'il est utile pour ce numéro
Au lieu de créer un Thread
normal, utilisez une API --- concurrent avancée comme ExecutorService ou ThreadPoolExecutor
Vous pouvez jeter un œil à différentes API dans cet article:
Java's Fork/Join vs ExecutorService - quand utiliser lequel?
Reportez-vous aux questions SE ci-dessous pour l'arrêt forcé d'ExecutorService: