Obtenez une liste de tous les threads en cours d'exécution en Java

232

Existe-t-il un moyen d'obtenir une liste de tous les threads en cours d'exécution dans la JVM actuelle (y compris les threads non démarrés par ma classe)?

Est-il également possible d'obtenir les objets Threadet Classde tous les threads de la liste?

Je veux pouvoir le faire via du code.

Kryten
la source

Réponses:

325

Pour obtenir un ensemble itérable:

Set<Thread> threadSet = Thread.getAllStackTraces().keySet();
thejoshwolfe
la source
19
Bien que beaucoup plus propre que l'autre alternative proposée, cela a pour inconvénient d'engager le coût d'obtention de traces de pile pour tous les threads. Si vous utilisez de toute façon ces traces de pile, c'est clairement supérieur. Sinon, cela peut être considérablement plus lent pour aucun gain autre que du code propre.
Eddie
29
@Eddie Est-ce une hypothèse de bon sens, ou avez-vous fait des expériences? "beaucoup plus lent" dites-vous; combien plus lent? Est-ce que ça vaut le coup? Je remets en question toute tentative d'aggraver le code pour des raisons d'efficacité. Si vous avez une exigence d'efficacité et une infrastructure pour mesurer l'efficacité quantitativement, alors je suis d'accord avec les gens qui aggravent le code, car ils semblent savoir ce qu'ils font. Voir la racine de tout mal selon Donald Knuth.
thejoshwolfe
20
Je n'ai pas chronométré ces alternatives spécifiques, mais j'ai travaillé avec d'autres moyens Java de collecte de traces de pile par rapport à une liste de threads. L'impact sur les performances semble dépendre très fortement de la JVM que vous utilisez (JRockit vs Sun JVM par exemple). Cela vaut la peine de mesurer dans votre cas spécifique. Que cela vous affecte ou non dépend de votre choix JVM et du nombre de threads dont vous disposez. J'ai trouvé que l'obtention de toutes les traces de pile via ThreadMXBean.dumpAllThreads pour environ 250 threads prenait 150 à 200 ms alors que la liste des threads (sans traces) n'était pas mesurable (0 ms).
Eddie
4
Sur mon système (Oracle Java 1.7 VM), une vérification rapide montre que cette méthode est ~ 70..80 fois PLUS LENTE que l'alternative ci-dessous. Les traces de pile et la réflexion appartiennent aux opérations Java les plus lourdes.
Franz D.
5
@thejoshwolfe: Bien sûr, la lisibilité est un facteur important, et il ne faut pas micro-optimiser etc. Cependant, j'ai fait mes recherches en écrivant un petit moniteur de performance d'application. Pour ce type d'outil, une empreinte de performance minimale est essentielle pour obtenir des données fiables, j'ai donc choisi la méthode sans trace de pile.
Franz D.
75

Obtenez une poignée à la racine ThreadGroup, comme ceci:

ThreadGroup rootGroup = Thread.currentThread().getThreadGroup();
ThreadGroup parentGroup;
while ((parentGroup = rootGroup.getParent()) != null) {
    rootGroup = parentGroup;
}

Maintenant, appelez la enumerate()fonction sur le groupe racine à plusieurs reprises. Le deuxième argument vous permet d'obtenir tous les threads, récursivement:

Thread[] threads = new Thread[rootGroup.activeCount()];
while (rootGroup.enumerate(threads, true ) == threads.length) {
    threads = new Thread[threads.length * 2];
}

Notez comment nous appelons enumerate () jusqu'à ce que le tableau soit suffisamment grand pour contenir toutes les entrées.

Frerich Raabe
la source
22
Je suis choqué que cette stratégie soit si populaire sur Internet. Ma stratégie est beaucoup plus simple (1 ligne de code) et fonctionne aussi bien avec l'avantage supplémentaire d'éviter les conditions de course.
thejoshwolfe
11
@thejoshwolfe: En fait, je suis d'accord - je pense que votre réponse est bien meilleure, et elle aurait probablement été la réponse acceptée en premier lieu si elle n'avait pas été en retard d'un an. Si l'OP fréquente toujours SO, ce qu'il fait apparemment, il serait bien avisé de ne pas accepter ma réponse et plutôt d'accepter la vôtre.
Frerich Raabe
2
Notez que pour tout autre chose que le rootGroup, vous devez utiliser new Thread[rootGroup.activeCount()+1]. activeCount()pourrait être nul, et si c'est le cas, vous courrez dans une boucle infinie.
jmiserez
7
@thejoshwolfe Je suppose que cette solution est beaucoup moins chère.
Haozhun
19
+1 pour cette réponse sous-estimée, car elle est beaucoup plus adaptée aux fins de surveillance à mon humble avis. Ses conditions de race inhérentes importent peu dans la surveillance. Cependant, comme l'ont montré certains tests quick'n'dirty, c'est environ 70 à 80 fois plus rapide que la solution basée sur stacktrace. Pour la surveillance, une petite empreinte de performance est essentielle, car vous voudrez garder les effets sur le système surveillé aussi petits que possible (Heisenberg frappe à nouveau :) Pour le débogage, où vous pouvez avoir besoin d'informations plus fiables, la méthode stacktrace pourrait être essentiel. BTW, la solution MxBean est encore plus lente que l'utilisation de stacktraces.
Franz D.
29

Oui, jetez un œil à l' obtention d'une liste de threads . Beaucoup d'exemples sur cette page.

C'est pour le faire par programme. Si vous voulez juste une liste sur Linux au moins, vous pouvez simplement utiliser cette commande:

kill -3 processid

et la VM fera un vidage des threads vers stdout.

cletus
la source
5
tuer -3? Au moins sur mon linux, c'est "terminal quit". Tue, ne fait pas de liste.
Michael H.
5
cletus est en effet correct - un kill -3 enverra le vidage à stdout, indépendamment de ce que le signal est censé signifier. J'envisagerais plutôt d'utiliser jstack.
Dan Hardiker du
Impossible d' obtenir une liste de threads : nadeausoftware.com a refusé de se connecter.
DSlomer64
14

Avez-vous jeté un œil à jconsole ?

Cela répertoriera tous les threads en cours d'exécution pour un processus Java particulier.

Vous pouvez démarrer jconsole à partir du dossier bin JDK.

Vous pouvez également obtenir une trace de pile complète pour tous les threads en appuyant Ctrl+Breaksur Windows ou en envoyant kill pid --QUITsous Linux.

pjp
la source
Je veux accéder à la liste dans ma classe java
Kryten
Dans ce cas, regardez la réponse de Cletus.
pjp
3
Pourquoi les gens votent-ils ainsi quand le gars a dit qu'il voulait une solution programmatique?
cletus
Parce que la question ne dit pas cela. Je vais modifier la question pour la rendre explicite.
pjp
8

Les utilisateurs d'Apache Commons peuvent utiliser ThreadUtils. L'implémentation actuelle utilise l'approche Walk the Thread Group décrite précédemment.

for (Thread t : ThreadUtils.getAllThreads()) {
      System.out.println(t.getName() + ", " + t.isDaemon());
}
gerardw
la source
7

Vous pouvez essayer quelque chose comme ceci:

Thread.getAllStackTraces().keySet().forEach((t) -> System.out.println(t.getName() + "\nIs Daemon " + t.isDaemon() + "\nIs Alive " + t.isAlive()));

et vous pouvez évidemment obtenir plus de caractéristiques de fil si vous en avez besoin.

hasskell
la source
5

Dans Groovy, vous pouvez appeler des méthodes privées

// Get a snapshot of the list of all threads 
Thread[] threads = Thread.getThreads()

En Java , vous pouvez invoquer cette méthode en utilisant la réflexion à condition que le gestionnaire de sécurité le permette.

Jarek Przygódzki
la source
Je reçois une erreur, getThreads n'est pas défini pour Thread. Et je ne vois pas cette fonction dans la documentation.
4

Extrait de code pour obtenir la liste des threads démarrés par le thread principal:

import java.util.Set;

public class ThreadSet {
    public static void main(String args[]) throws Exception{
        Thread.currentThread().setName("ThreadSet");
        for ( int i=0; i< 3; i++){
            Thread t = new Thread(new MyThread());
            t.setName("MyThread:"+i);
            t.start();
        }
        Set<Thread> threadSet = Thread.getAllStackTraces().keySet();
        for ( Thread t : threadSet){
            if ( t.getThreadGroup() == Thread.currentThread().getThreadGroup()){
                System.out.println("Thread :"+t+":"+"state:"+t.getState());
            }
        }
    }
}

class MyThread implements Runnable{
    public void run(){
        try{
            Thread.sleep(5000);
        }catch(Exception err){
            err.printStackTrace();
        }
    }
}

production:

Thread :Thread[MyThread:2,5,main]:state:TIMED_WAITING
Thread :Thread[MyThread:0,5,main]:state:TIMED_WAITING
Thread :Thread[MyThread:1,5,main]:state:TIMED_WAITING
Thread :Thread[ThreadSet,5,main]:state:RUNNABLE

Si vous avez besoin de tous les threads, y compris les threads système, qui n'ont pas été démarrés par votre programme, supprimez la condition ci-dessous.

if ( t.getThreadGroup() == Thread.currentThread().getThreadGroup())

Maintenant sortie:

Thread :Thread[MyThread:2,5,main]:state:TIMED_WAITING
Thread :Thread[Reference Handler,10,system]:state:WAITING
Thread :Thread[MyThread:1,5,main]:state:TIMED_WAITING
Thread :Thread[ThreadSet,5,main]:state:RUNNABLE
Thread :Thread[MyThread:0,5,main]:state:TIMED_WAITING
Thread :Thread[Finalizer,8,system]:state:WAITING
Thread :Thread[Signal Dispatcher,9,system]:state:RUNNABLE
Thread :Thread[Attach Listener,5,system]:state:RUNNABLE
Ravindra babu
la source
3
    public static void main(String[] args) {


        // Walk up all the way to the root thread group
        ThreadGroup rootGroup = Thread.currentThread().getThreadGroup();
        ThreadGroup parent;
        while ((parent = rootGroup.getParent()) != null) {
            rootGroup = parent;
        }

        listThreads(rootGroup, "");
    }


    // List all threads and recursively list all subgroup
    public static void listThreads(ThreadGroup group, String indent) {
        System.out.println(indent + "Group[" + group.getName() + 
                ":" + group.getClass()+"]");
        int nt = group.activeCount();
        Thread[] threads = new Thread[nt*2 + 10]; //nt is not accurate
        nt = group.enumerate(threads, false);

        // List every thread in the group
        for (int i=0; i<nt; i++) {
            Thread t = threads[i];
            System.out.println(indent + "  Thread[" + t.getName() 
                    + ":" + t.getClass() + "]");
        }

        // Recursively list all subgroups
        int ng = group.activeGroupCount();
        ThreadGroup[] groups = new ThreadGroup[ng*2 + 10];
        ng = group.enumerate(groups, false);

        for (int i=0; i<ng; i++) {
            listThreads(groups[i], indent + "  ");
        }
    }
Codeur ZZ
la source
3

Dans la console java, appuyez sur Ctrl-Break . Il répertorie tous les threads ainsi que quelques informations sur le tas. Cela ne vous donnera pas accès aux objets bien sûr. Mais cela peut être très utile pour le débogage de toute façon.

raoulsson
la source
1

Pour obtenir une liste des threads et leurs états complets à l'aide du terminal, vous pouvez utiliser la commande ci-dessous:

jstack -l <PID>

Quel PID est l'ID du processus en cours d'exécution sur votre ordinateur. Pour obtenir l'ID de processus de votre processus java, vous pouvez simplement exécuter la jpscommande.

En outre, vous pouvez analyser votre vidage de threads produit par jstack dans TDAs (Thread Dump Analyzer) tel un outil d'analyse de threads rapide ou spotify .

Amir Fo
la source