3

当对大部分空闲的 Tomcat 服务器进行线程转储时,很多线程可能会显示为 RUNNABLE 状态,如下所示:

"http-bio-8443-exec-21975" daemon prio=10 tid=0x00007f6f6406c000 nid=0x222a runnable [0x00007f6f156ae000]
   java.lang.Thread.State: RUNNABLE
        at java.net.SocketInputStream.socketRead0(Native Method)
        at java.net.SocketInputStream.read(SocketInputStream.java:129)
        at org.apache.coyote.http11.InternalInputBuffer.fill(InternalInputBuffer.java:516)
        at org.apache.coyote.http11.InternalInputBuffer.fill(InternalInputBuffer.java:501)
        at org.apache.coyote.http11.Http11Processor.setRequestLineReadTimeout(Http11Processor.java:173)
        at org.apache.coyote.http11.AbstractHttp11Processor.process(AbstractHttp11Processor.java:924)
        at org.apache.coyote.AbstractProtocol$AbstractConnectionHandler.process(AbstractProtocol.java:565)
        at org.apache.tomcat.util.net.JIoEndpoint$SocketProcessor.run(JIoEndpoint.java:309)
        - locked <0x00000007cadcd3f8> (a org.apache.tomcat.util.net.SocketWrapper)
        at java.util.concurrent.ThreadPoolExecutor$Worker.runTask(ThreadPoolExecutor.java:895)
        at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:918)
        at java.lang.Thread.run(Thread.java:662)

我对源代码的解释是,这个线程正在等待(超时)来自 HTTP keepalive 连接的更多数据。因此,即使线程是 RUNNABLE,它也不会消耗 CPU。

Thread.State RUNNABLE javadoc说:

处于可运行状态的线程正在 Java 虚拟机中执行,但它可能正在等待来自操作系统的其他资源,例如处理器。

所以在这种情况下,其他资源将是 I/O 而不是 CPU。

在其他问题Java socketRead0 Issue中,Geoff回答:

我相信,当您使用 Java 本机方法时,即使调用实际上被阻塞等待某个事件,堆栈跟踪也会显示 RUNNABLE。从本质上讲,我不相信 Java 有任何方法知道本地方法实际上在做什么,因此它将这些调用标记为 RUNNABLE。我已经在 socketRead0() 和 socketAccept() 中看到了这一点——它们通常都会阻塞。

我得出了类似的结论,我想在这个专门的问题中验证这种解释,即:

如果我想通过查看 RUNNABLE Threads 来分析 CPU 消耗,我可能不得不通过非常仔细地查看它们的源代码来排除 Native Methods 中的线程?

关键是它不像只查看线程的状态那么容易,而是必须深入研究源代码并开始猜测特定的本地方法可能在做什么(甚至查看它的 C 或 C++ 源代码)。

4

2 回答 2

1

It's OK to Ask and Answer Your Own Questions中,我的回答是肯定的,这可能是必要的,到目前为止遇到的本机方法的一些概要可能会很有用。

于 2014-10-20T09:39:58.937 回答
0

如果我想通过查看 RUNNABLE Threads 来分析 CPU 消耗,我可能不得不通过非常仔细地查看它们的源代码来排除 Native Methods 中的线程?

您可能排除它们。但总的来说,你不能。本机方法可以在两种状态之间交替,在这种状态下,它要么被阻塞,要么消耗大量 CPU。即使您详细检查了代码,也无法仅从线程堆栈转储中判断它处于哪个状态。

(如果你能掌握原生 PC,你也许可以......但即使这样也需要付出巨大的努力才能实现。)


当然,标准库中有很多本地方法几乎不消耗 CPU,您当然可以在分析中使用它们。但是,还有其他标准本机库可能会表现出“混合模式”行为;例如原生ZIP 库和一些在AWT、SWT 等中使用的原生库。

于 2014-10-20T09:55:27.320 回答