如何获取线程的堆栈跟踪

9

我有一个多线程应用程序。该应用程序接收并在不同的线程中处理多个消息。为此,我使用了java.util.concurrent包中的ThreadPoolExecutor和FutureTask类。

偶尔会发生死锁。当发生死锁时,我想中断被阻塞的线程,并记录该线程的堆栈跟踪,以便稍后解决死锁问题。

在Java中,是否有一种方法可以在该线程之外找到线程的堆栈跟踪?


你的意思是想要检测死锁并进行恢复吗?我从未尝试过这样做,但使用堆栈跟踪似乎不是正确的方法。 - Enno Shioji
我需要堆栈跟踪,以便我知道线程卡在哪里,并且可以修复问题,以便在重新启动后应用程序正常工作。但是,我想中断阻塞的线程,以便在找到时间修复问题并发布之前,应用程序可以正常工作。 - Palo
6个回答

5

您可以在应用程序内部定期记录所有线程的堆栈跟踪(或在终止进程之前),使用以下方法:

Map<Thread, StackTraceElement[]> m = Thread.getAllStackTraces();
for(Map.Entry<Thread,  StackTraceElement[]> e : m.entrySet()) {
    log(e.getKey().toString());
    for (StackTraceElement s : e.getValue()) {
        log("  " + s);
    }
}

在运行夜间自动化测试时,有时候会发生某个测试用例进入死锁的情况。我添加了一个“TimeBomb”守护线程,等待30分钟,如果仍然出现死锁,则记录所有堆栈跟踪,如上所述。


5

请参阅此处了解如何生成堆栈跟踪,包括如何以编程方式执行此操作。从控制台,使用Ctrl+Break将堆栈跟踪转储到标准输出。有关更多详细信息,请参见此 SO 问题


5
在 UNIX 环境下,向 Java 进程发送“QUIT”信号(kill -QUIT <pid>)也将在标准输出中输出堆栈跟踪信息。 - Bombe

4

在进入死锁区域之前,设置一个字段,例如:

thread = Thread.currentThread();

在你的监控线程中,你可以随时执行thread.getStackTrace();来获取该线程的堆栈跟踪。

2
你使用JStack。这里有一篇不错的博客文章详细介绍如何获取堆栈跟踪。请参考此链接

0

我不确定您是否希望从同一JVM内部获取堆栈跟踪,还是希望使用外部工具获取堆栈跟踪,但如果您希望使用外部工具获取堆栈跟踪,则以下内容将有所帮助:

  • Java VisualVM 工具可用于连接到正在运行的JVM,其中可以转储线程堆栈。这通常是大多数使用Java 6的人首选的方法。启动VisualVM后,可以通过在应用程序选项卡中选择进程来获取该进程的线程转储。现在会提供一个线程选项卡以查看正在运行的进程中的线程,在同一选项卡中,您将找到“线程转储”按钮以提取所需信息。
  • JDK中的jstack实用程序也可用于生成线程堆栈跟踪。

0
当死锁发生时,我想要中断阻塞的线程...
你可以实现一个定期任务来检查死锁(其中死锁是基于Java内部或Lock的),并在涉及到该场景的所有线程上调用interrupt。然而,这并不能保证解决问题。很可能情况会再次发生。有关详细信息,请参见Dr Heinz的死锁检测器文章
事实上,没有保证interrupt甚至能够释放像这样被阻塞的进程。避免死锁场景的更好方法是首先使用带有超时和重试策略或“先试后买”方法的锁。
我想记录此线程的堆栈跟踪...
如果您想以编程方式执行此操作,请再次遵循Dr Heinz的示例。如果不是,请在发现问题时生成线程转储。
有没有办法在Java中在该线程之外找到线程的堆栈跟踪?

是和不是。您可以从其他虚拟机中转储线程,但它们的堆栈跟踪可能并不像您认为的那样有用,以确定死锁原因。如果 JVM(在您的应用程序虚拟机上)检测到了真正的死锁,您应该拥有调试原因所需的一切(或多或少)。


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