Obtenga una lista de todos los hilos que se ejecutan actualmente en Java

232

¿Hay alguna manera de obtener una lista de todos los hilos en ejecución en la JVM actual (incluidos los hilos no iniciados por mi clase)?

¿También es posible obtener los objetos Thready Classde todos los hilos en la lista?

Quiero poder hacer esto a través del código.

Kryten
fuente

Respuestas:

325

Para obtener un conjunto iterable:

Set<Thread> threadSet = Thread.getAllStackTraces().keySet();
thejoshwolfe
fuente
19
Si bien es mucho más limpio que la otra alternativa propuesta, esto tiene el inconveniente de incurrir en el costo de obtener rastros de pila para todos los hilos. Si va a utilizar esos rastros de pila de todos modos, esto es claramente superior. Si no, entonces esto puede ser significativamente más lento para ninguna ganancia que no sea un código limpio.
Eddie
29
@ Eddie ¿Es eso una suposición del sentido común, o hiciste experimentos? "significativamente más lento" dices; cuanto mas lento ¿Vale la pena? Cuestiono cualquier intento de empeorar el código en aras de la eficiencia. Si tiene un requisito de eficiencia y una infraestructura para medir la eficiencia cuantitativamente, entonces estoy de acuerdo con que las personas empeoren el código, porque parecen saber lo que están haciendo. Vea la raíz de todo mal según Donald Knuth.
thejoshwolfe
20
No he cronometrado estas alternativas específicas, pero he trabajado con otros medios de Java para recopilar trazas de pila frente a solo una lista de hilos. El impacto en el rendimiento parece depender mucho de qué JVM está utilizando (JRockit vs Sun JVM, por ejemplo). Vale la pena medir en su instancia específica. Si te afectará o no depende de tu elección de JVM y de cuántos hilos tengas. Descubrí que obtener todos los rastros de la pila a través de ThreadMXBean.dumpAllThreads durante aproximadamente 250 subprocesos tomará 150-200 mseg mientras se obtiene solo la lista de subprocesos (sin trazas) para que no sea medible (0 mseg).
Eddie
44
En mi sistema (Oracle Java 1.7 VM), una comprobación rápida muestra que este método es ~ 70..80 veces MÁS LENTO que la alternativa a continuación. Las trazas de pila y la reflexión pertenecen a las operaciones Java más pesadas.
Franz D.
55
@thejoshwolfe: Por supuesto, la legibilidad es un factor importante, y uno no debe micro-optimizar, etc. Sin embargo, hice mi investigación mientras escribía un pequeño monitor de rendimiento de la aplicación. Para este tipo de herramienta, una huella de rendimiento mínima es esencial para obtener datos confiables, por lo que elegí el método sin seguimiento de stacktrace.
Franz D.
75

Obtenga un identificador para la raíz ThreadGroup, así:

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

Ahora, llame a la enumerate()función en el grupo raíz repetidamente. El segundo argumento le permite obtener todos los hilos, de forma recursiva:

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

Observe cómo llamamos a enumerate () repetidamente hasta que la matriz sea lo suficientemente grande como para contener todas las entradas.

Frerich Raabe
fuente
22
Me sorprende que esta estrategia sea tan popular en Internet. Mi estrategia es mucho más simple (1 línea de código) y funciona igual de bien con la ventaja adicional de evitar las condiciones de carrera.
thejoshwolfe
11
@thejoshwolfe: En realidad, estoy de acuerdo, creo que su respuesta es mucho mejor, y probablemente habría sido la respuesta aceptada en primer lugar si no hubiera sido un año tarde. Si el OP todavía frecuenta SO, lo que aparentemente hace, sería aconsejable que no acepte mi respuesta y prefiera aceptar la suya.
Frerich Raabe
2
Tenga en cuenta que para cualquier otra cosa que no sea rootGroup, debe usar new Thread[rootGroup.activeCount()+1]. activeCount()podría ser cero, y si lo es, te encontrarás con un bucle infinito.
jmiserez
77
@thejoshwolfe Supongo que esta solución es mucho menos costosa.
Haozhun
19
+1 para esta respuesta subestimada, ya que es mucho más adecuado para fines de monitoreo en mi humilde opinión. Sus condiciones de carrera inherentes no importan mucho en el monitoreo. Sin embargo, como mostró una prueba rápida y sucia, es aproximadamente 70-80 veces más rápida que la solución basada en stacktrace. Para el monitoreo, es esencial una pequeña impresión de rendimiento, ya que querrá mantener los efectos en el sistema monitoreado lo más pequeños posible (Heisenberg ataca de nuevo :) Para la depuración, donde puede necesitar información más confiable, el método stacktrace podría ser esencial. Por cierto, la solución MxBean es aún más lenta que el uso de stacktraces.
Franz D.
29

Sí, eche un vistazo para obtener una lista de hilos . Muchos ejemplos en esa página.

Eso es hacerlo programáticamente. Si solo desea una lista en Linux, al menos puede usar este comando:

kill -3 processid

y la VM hará un volcado de subprocesos a stdout.

cletus
fuente
55
matar -3? Al menos en mi Linux, eso es "terminal quit". Mata, no enumera.
Michael H. el
55
cletus es realmente correcto: un kill -3 hará un volcado de subprocesos a stdout, independientemente de lo que se suponga que signifique la señal. Consideraría usar jstack en su lugar.
Dan Hardiker
no se puede obtener una lista de hilos : nadeausoftware.com se negó a conectarse.
DSlomer64
14

¿Has echado un vistazo a jconsole ?

Esto enumerará todos los hilos que se ejecutan para un proceso Java en particular.

Puede iniciar jconsole desde la carpeta bin JDK.

También puede obtener un seguimiento completo de la pila para todos los hilos presionando Ctrl+Breaken Windows o enviando kill pid --QUITen Linux.

pjp
fuente
Quiero acceder a la lista dentro de mi clase de Java
Kryten
En cuyo caso, mira la respuesta de cletus.
pjp
3
¿Por qué la gente vota esto cuando el tipo dijo que quería una solución programática?
cletus
Porque la pregunta no dice eso. Editaré la pregunta para que sea explícita.
pjp
8

Los usuarios de Apache Commons pueden usar ThreadUtils. La implementación actual utiliza el enfoque de caminar por el grupo de subprocesos descrito anteriormente.

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

Puedes probar algo como esto:

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

y obviamente puede obtener más características de hilo si lo necesita.

Hasskell
fuente
5

En Groovy puedes llamar a métodos privados

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

En Java , puede invocar ese método utilizando la reflexión siempre que el administrador de seguridad lo permita.

Jarek Przygódzki
fuente
Me sale un error, getThreads no está definido para Thread. Y no veo esta función en la documentación.
4

Fragmento de código para obtener una lista de hilos iniciados por el hilo 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();
        }
    }
}

salida:

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 necesita todos los hilos, incluidos los hilos del sistema, que su programa no ha iniciado, elimine la siguiente condición.

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

Ahora salida:

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
fuente
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 + "  ");
        }
    }
Codificador ZZ
fuente
3

En la consola de Java, presiona Ctrl-Break . Enumerará todos los subprocesos más información sobre el montón. Esto no le dará acceso a los objetos, por supuesto. Pero puede ser muy útil para la depuración de todos modos.

Raoulsson
fuente
1

Para obtener una lista de subprocesos y sus estados completos utilizando el terminal, puede usar el siguiente comando:

jstack -l <PID>

Qué PID es la identificación del proceso que se ejecuta en su computadora. Para obtener la identificación del proceso de su proceso de Java, simplemente puede ejecutar el jpscomando.

Además, puede analizar su volcado de subprocesos producido por jstack en TDA (Thread Dump Analyzer), como la herramienta de análisis de subprocesos o subprocesos rápidos .

Amir Fo
fuente