当对大部分空闲的 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++ 源代码)。