我如何检测我的方法在等待什么?

How do I detect what is my method waiting for?

我在 Java 中有一个方法可以调用其他几个方法。在固定线程池中从多个线程调用此方法。工作人员的数量与可用处理器(核心)的数量相同。

public void query() {
    method1();
    method2();
}

当我使用 VisualVM 分析程序执行时,method1()method2() 的时间非常短,但是 query() 的自拍时间非常长。但是该方法除了两次调用之外没有其他代码。 method1()method2() 内部可能存在同步,但我控制的代码中没有明显的同步。

当我将pool中的worker数量减少到1时,这个self-time就快没了。整个程序的单线程和多线程执行时间几乎是一样的。我认为这意味着我的方法 query() 正在等待某些东西。

没有死锁,执行完成。 method1()method2()这两个方法在混淆的jar中调用了很多其他的东西,包括库类,所以我调试起来并不容易。但是 query() 方法是直接从工作线程调用的,使用 java.util.concurrent.ExecutorService.

您最好的选择是找到一种方法来获取 运行 程序的堆栈跟踪。 Here 是一种可能的方法。

在级别 3 对 运行 进程发出 kill 命令。所有线程都将堆栈跟踪转储到标准输出,应用程序将继续 运行。

kill -3 <pid>

请注意,您不会在发出 kill 命令的控制台上看到任何内容。 Java 应用程序本身将具有输出。您可能需要检查日志,具体取决于应用重定向其输出的位置。

我建议 运行 在您的 IDE 中使用调试模式的程序,并在可能出现问题的地方放置断点。然后在程序延迟的地方进入(例如 Netbeans 中的 F7)。尽管您可能无法在那里解决问题,但您可以一直介入到混淆代码。但是你会知道延迟在哪里。

我在代理 class 中发现了问题,该代理 class 将另一个 class 包装在自定义锁定机制中。

我继续创建一系列线程转储。由于我使用 JVisualVM 进行分析,因此在此过程中我创建了一些线程转储。 Ctrl+Break 也有效,与 Synesso 在他的回答中提到的 kill -3 <pid> 相同。

我是用评论里提到的Thread Dump Analyzer来分析的。我不知道首先要寻找什么,但是由于 TDA 中对象和监视器的链接,我找到了这样的东西:

"pool-9-thread-32" #304 prio=5 os_prio=0 tid=0x000000002a706800 nid=0x348c waiting for monitor entry [0x000000003f06e000]
   java.lang.Thread.State: BLOCKED (on object monitor)
    at com.example.MyClass.method1(MyClass.java:400)
    - waiting to lock <0x0000000680837b90> (a com.example.DifferentClass)
    at com.example.MyClass.query(MyClass.java:500)
    ... omitted ...
    at java.util.concurrent.FutureTask.run(FutureTask.java:270)
    at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:618)
    at java.lang.Thread.run(Thread.java:745)

   Locked ownable synchronizers:
    - <0x000000075bc59aa8> (a java.util.concurrent.ThreadPoolExecutor$Worker)

DifferentClass extends abstract MyClass 并且有一个从 method1()DifferentClass 的调用,其中一个 DTO 对象被传递给一个进行大量处理的方法,记录并最终保存到数据库。在创建处理 classes.

的数据库之一期间使用了代理 class