分析多线程 Java 应用程序

Posted

技术标签:

【中文标题】分析多线程 Java 应用程序【英文标题】:Analysing a multi-threaded Java application 【发布时间】:2011-11-17 15:05:36 【问题描述】:

在我参与的一个开源应用程序中,我们遇到了一个错误,即应用程序并不总是正确关闭。这就是我想解决的问题。

经验表明,大多数情况下,当线程和进程正在启动但管理不正确时(例如,线程正在等待套接字连接,应用程序正在关闭并且线程继续等待),就会发生这种情况。 考虑到这一点,我在整个源代码中搜索了“.start()”,发现了 53 次(这让我有点害怕)。 作为第一步,我想创建一个辅助类(ThreadExecutor),其中当前代码“thread.start()”将被“ThreadExecutor.Execute(thread)”替换,以便a)在现有源中仅进行一些更改b) 一个类,我可以在其中轻松检查哪些线程没有按应有的方式结束。为此,我想

调用Execute方法时将要执行的线程添加到一个名为activeThreads的列表中 启动线程 结束时将其从 activeThreads 列表中删除。

这样我就有一个所有正在执行的线程的最新列表,当应用程序在关机时挂起时,我可以在那里看到是哪个线程导致它。

问题:

您如何看待这个概念?我通常在编写 c# 并且知道如何使用 .NET 和 worker 来完成它,但我不太确定 Java 中什么是最好的(我想在现有源代码中修改尽可能少的代码行)。 如果这个概念看起来不错,我怎样才能得到线程终止的通知。我想避免有一个额外的线程每隔一段时间检查一下 activeThreads 中包含的所有线程的状态,如果它们终止则删除它们。

澄清一下:在弄清楚如何正确终止应用程序之前,我在这里要问的是,对于某些难以重现的测试用例,找出哪些线程是原因的最佳/最简单方法是什么。

【问题讨论】:

如果你让线程守护进程——当应用程序被杀死时它们不会被杀死吗? download.oracle.com/javase/6/docs/api/java/lang/… 您是否尝试过在应用程序未正常关闭时进行线程转储以查看哪些线程被卡住以及它们在做什么?这可能会给你一些线索。更多信息:java.sun.com/developer/technicalArticles/Programming/Stacktrace 不,还没有进行线程转储。我会检查这两个建议。通常我更喜欢自己管理线程的生命周期,而不是不管线程状态如何都杀死所有东西。这让我感觉更有控制力:) 找出哪些线程行为不端是解决问题的第一种方法,线程转储将提供所有必要的线索。 【参考方案1】:

我会在更改任何代码之前尝试分析您的应用程序的行为。代码更改可能会引入新问题 - 如果您尝试解决问题,则不是您想要做的。

关于当前正在运行的线程自省应用程序状态的最简单方法是获取线程转储。您说您的问题是应用程序在关机时挂起。这是应用线程转储的完美场景。您将能够看到哪些线程被阻塞了。

您可以阅读有关线程转储的更多信息here。

【讨论】:

【参考方案2】:

尝试使所有线程成为守护进程(当所有剩余线程都成为守护进程时,JVM 终止)。在启动每个线程之前使用thread.setDaemon(true)。

【讨论】:

这听起来就像是在你的应用程序的脸上射击,所以它肯定会死。 这不正是 OP 想要的吗?线程中有一些进程在某处持续监听。应用程序关闭,进程自动终止。 让我这样说:OP 说存在应用程序无法正确终止的错误。我的猜测是这个错误并不是所有的线程都是守护线程。可能存在阻止应用程序关闭的线程。这些线程大概有一些重要的工作要做,而让它们成为守护线程只是为了让应用程序终止可能不会解决问题。 @Paul Morie:但为什么会有这样的猜测?我不止一次看到 Java 应用程序没有退出,因为在不重要的线程上很容易忘记 setDaemon(true)。只要您至少控制了一个非守护线程,并且,套用您的话,不要“对着那个线程开枪”,你就不是“在开枪打你的应用程序在脸上” [原文如此]。正如 Petar Minchev 指出的那样:这基本上是守护线程背后的全部想法。现在,如果 OP 的线程应该是守护线程,我不知道......但我仍然认为 Petar 的观点是有效的。 这可能是一个简单的解决方案,但不是我喜欢的。必须检查进程(例如,使用 mencoder 对视频进行转码)如何处理【参考方案3】:

您可以尝试使用jvisualvm(随jdk 提供,在jdk 的bin 文件夹中找到它)查看您的应用程序。 JVisualVM 可以连接到您的应用程序并显示很多有趣的信息,包括哪些进程仍在运行。在开始您描述的道路之前,我会先试一试。

如果您需要,这里有一些 JVisualVM 上的 documentation。

【讨论】:

【参考方案4】:

java 中最好的方法是直接使用Thread pools 而不是线程(尽管直接使用线程是可以接受的)。线程池接受Runnable 对象,您可以将其视为任务。这个想法是大多数线程会做一个小任务然后结束,因为制作Thread 是昂贵的并且更难管理你可以使用线程池,它允许像'ThreadPoolExecutor.awaitTermination()'这样的事情。如果池中的任务多于线程数,则剩余任务将仅排队。

Thread 更改为Runnable 很容易,您甚至可以在自己创建的线程上执行一个可运行文件。

请注意,这可能不适用于长时间运行的线程,但您的问题似乎表明它们最终会完成。

关于你的第二个问题,找出哪些线程在某个点运行的最佳方法是在调试器(如 Eclipse)中运行应用程序,并在关闭函数的断点处暂停所有线程。

【讨论】:

【参考方案5】:

我会尝试 jprofiler 的试用版或类似的东西,它可以让您深入了解您的应用程序及其线程的实际作用。

暂时不要更改代码,但尝试重现并了解何时发生这种情况。

【讨论】:

【参考方案6】:

为自己创建一个静态线程池。

static ExecutorService threads = Executors.newCachedThreadPool();

对于每次线程更改的开始:

new Thread(new AThread()).start();

threads.submit(new AThread ());

当您的代码退出时,列出所有正在运行的线程:

List<Runnable> runningThreads = threads.shutdownNow();
for ( Runnable t : runningThreads ) 
  System.out.println("Thread running at shutdown: "+t.toString());

这不仅会关闭所有正在运行的线程,还会列出它们以供您查看它们的问题。

编辑:添加

如果您想跟踪所有正在运行的线程,请使用:

Future f = threads.submit(new AThread ());

并将其存储在某个列表中。然后,您可以通过以下调用了解其状态:

f.isDone();
... etc.

【讨论】:

缓存线程池,又名有史以来最可怕的发明之一。最好确保你明白你在那里做什么。 同意 Voo,但是 OP 想要一些不显眼的东西,这很容易用作不受控制的线程使用的替代品。一旦问题得到解决,我强烈建议他们使用一个或多个FixedThreadPools。

以上是关于分析多线程 Java 应用程序的主要内容,如果未能解决你的问题,请参考以下文章

Java程序员必备!Java底层分析多线程行为附答案

java多线程的深入分析

Java多线程-程序运行堆栈分析

java线程上下文切换,用于理解java程序cpu损耗分析。

Java多线程:并发死锁问题分析资源限制的挑战

Java多线程 -- JUC包源码分析11 -- ThreadPoolExecutor源码分析