获取当前在Java中正在运行的所有线程列表

273

有没有办法获取当前JVM中所有运行中线程的列表(包括未由我的类启动的线程)?

是否还可以在列表中获取所有线程的ThreadClass对象?

我希望能够通过代码实现此操作。

13个回答

385

获取可迭代的集合:

Set<Thread> threadSet = Thread.getAllStackTraces().keySet();

性能:12个线程,耗时0毫秒(Azul JVM 16.0.1,Windows 10,Ryzen 5600X)。


24
虽然比提出的另一种方案要干净得多,但这种方法的缺点是需要为所有线程获取堆栈跟踪成本。如果您将使用这些堆栈跟踪,那么这显然更优秀。如果不会使用,那么除了代码更整洁外,可能没有其他好处,而且速度可能会慢很多。 - Eddie
35
“@Eddie你说的‘显著慢’是从常识出发的假设还是经过实验证明的?‘慢’有多慢?这值得吗?我对任何为了效率而使代码变差的尝试都持怀疑态度。如果您必须满足效率要求,而且有基础设施可以定量评估效率,那么我同意人们可以让代码变差,因为他们似乎知道自己在做什么。请参阅Donald Knuth所说的‘所有恶之根源’。” - thejoshwolfe
22
我没有计时这些具体的替代方案,但我已经使用了其他Java方法来收集堆栈跟踪而不仅仅是线程列表。性能影响似乎很大程度上取决于您使用的JVM(例如JRockit vs Sun JVM)。在你特定的情况下进行测量是值得的。它是否会影响您取决于您的JVM选择和您拥有多少线程。我发现通过ThreadMXBean.dumpAllThreads获取大约250个线程的所有堆栈跟踪需要150-200毫秒,而仅获取线程列表(无跟踪)不可测量(0毫秒)。 - Eddie
4
在我的系统上(Oracle Java 1.7 VM),快速检查显示,这个方法比下面的替代方法慢大约70..80倍。堆栈跟踪和反射属于Java中最耗费资源的操作。 - Franz D.
5
当然,易读性是一个重要的因素,不应该过分优化等。但是,在编写小型应用程序性能监视器时,我进行了研究。对于这种类型的工具,最小化性能印记是获取可靠数据所必需的,因此我选择了不包含堆栈跟踪的方法。 - Franz D.
显示剩余8条评论

87

获取根ThreadGroup的句柄,如下所示:

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

现在,反复调用 enumerate() 函数并将其作用于根组。第二个参数让你递归获取所有线程:

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

注意我们如何重复调用enumerate()方法,直到数组足够大以容纳所有条目。


23
我震惊于这种策略在互联网上如此受欢迎。我的策略更简单(只需一行代码),并且具有避免竞态条件的额外优势,同样有效。 - thejoshwolfe
14
@thejoshwolfe:实际上,我同意 - 我认为你的回答更好,如果它不是一年迟到的话,它可能本来就是被接受的答案。如果原文提问者仍然经常访问SO,他最好取消接受我的答案,而接受你的。 - Frerich Raabe
4
请注意,除了rootGroup以外的任何情况下,您应该使用new Thread[rootGroup.activeCount()+1]activeCount()可能为零,如果是这样,您将陷入无限循环中。 - jmiserez
9
我想这个解决方案要便宜得多。 - Haozhun
23
这个被低估的答案值得点赞,因为在我看来它更适合于监控目的。尽管它固有的竞态条件在监控中并不重要,但一些快速而脏的测试显示,它比基于堆栈跟踪的解决方案快70-80倍。对于监控来说,小的性能印记是必要的,因为你希望尽可能地减少对被监控系统的影响(海森堡再次出现 :))。对于调试,你可能需要更可靠的信息,堆栈跟踪方法可能是必不可少的。顺便说一句,MxBean解决方案甚至比使用堆栈跟踪还要慢。 - Franz D.
显示剩余2条评论

31

是的,看一下获取线程列表。该页面上有很多示例。

这是以编程方式完成的。如果你只想在Linux上列出一个列表,至少可以使用以下命令:

kill -3 processid

此时虚拟机将在标准输出中输出线程转储信息。


5
“kill -3” 在我的 Linux 上是“终端退出”。它会终止进程,而不是列出进程。请注意,这里没有解释。 - Michael H.
6
克利图斯说得没错——kill -3会将线程转储输出到标准输出,无论信号的本意是什么。我建议考虑使用jstack代替。 - Dan Hardiker
1
无法访问获取线程列表:nadeausoftware.com拒绝连接。 - DSlomer64

20

15

您看过 jconsole 吗?

它可以列出特定 Java 进程中所有正在运行的线程。

您可以从 JDK bin 文件夹启动 jconsole。

您也可以通过在 Windows 上按下 Ctrl + Break 或在 Linux 上发送 kill pid --QUIT 来获取所有线程的完整堆栈跟踪。


我想要在我的Java类中访问列表。 - Kryten
在这种情况下,请查看Cletus的答案。 - pjp
3
这位人说他想要一个程序化的解决方案,那么为什么人们要投赞成票呢?我不太明白。 - cletus
因为问题没有说明。我会编辑问题,使其明确。 - pjp

14
您可以尝试类似以下的操作:
Thread.getAllStackTraces().keySet().forEach((t) -> System.out.println(t.getName() + "\nIs Daemon " + t.isDaemon() + "\nIs Alive " + t.isAlive()));

如果需要,您可以明显获得更多的线程特性。


12

Apache Commons用户可以使用ThreadUtils。当前实现使用先前概述的遍历线程组方法。

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

11
使用终端获取线程以及它们的完整状态列表,您可以使用以下命令:
jstack -l <PID>

在您的计算机上运行的进程的ID是哪个 <PID>。要获取Java进程的进程ID,您可以简单地运行jps命令。

此外,您还可以分析由jstack生成的线程转储,例如使用TDAs(线程转储分析器)中的fastthreadSpotify线程分析工具


6

获取由主线程启动的线程列表的代码片段:

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();
        }
    }
}

输出:

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

如果您需要包括由系统启动而非由您的程序启动的所有线程,请删除以下条件。
if ( t.getThreadGroup() == Thread.currentThread().getThreadGroup())

现在输出:
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

5
Groovy 中,您可以调用私有方法。
// Get a snapshot of the list of all threads 
Thread[] threads = Thread.getThreads()

在Java中,如果安全管理器允许,您可以使用反射来调用该方法。

我遇到了一个错误,Thread中未定义getThreads函数。而且我在文档中也没有看到这个函数。 - user9599745

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接