我们知道java线程使用的是底层操作系统"轻量级线程"+"内核线程"的1:1模型,那么我们如何查看一个运行的java进程中线程数呢?
1、linux上查看进程中线程数:
主要有以下三种方法:
1)top -Hp pid
$ top -Hp 12121
top - 13:37:23 up 223 days, 18:41, 2 users, load average: 0.01, 0.02, 0.05
Threads: 20 total, 0 running, 20 sleeping, 0 stopped, 0 zombie
%Cpu(s): 0.1 us, 0.2 sy, 0.0 ni, 99.7 id, 0.0 wa, 0.0 hi, 0.0 si, 0.0 st
KiB Mem : 16267876 total, 549064 free, 3220720 used, 12498092 buff/cache
KiB Swap: 2097148 total, 2034684 free, 62464 used. 11924764 avail Mem
PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND
12121 root 20 0 6963564 26104 10532 S 0.0 0.2 0:00.00 java
12122 root 20 0 6963564 26104 10532 S 0.0 0.2 0:00.10 java
12123 root 20 0 6963564 26104 10532 S 0.0 0.2 0:00.00 java
12124 root 20 0 6963564 26104 10532 S 0.0 0.2 0:00.00 java
12125 root 20 0 6963564 26104 10532 S 0.0 0.2 0:00.00 java
12126 root 20 0 6963564 26104 10532 S 0.0 0.2 0:00.00 java
12127 root 20 0 6963564 26104 10532 S 0.0 0.2 0:00.00 java
12128 root 20 0 6963564 26104 10532 S 0.0 0.2 0:00.00 java
12129 root 20 0 6963564 26104 10532 S 0.0 0.2 0:00.00 java
12130 root 20 0 6963564 26104 10532 S 0.0 0.2 0:00.00 java
12131 root 20 0 6963564 26104 10532 S 0.0 0.2 0:00.00 java
12132 root 20 0 6963564 26104 10532 S 0.0 0.2 0:00.00 java
12133 root 20 0 6963564 26104 10532 S 0.0 0.2 0:00.00 java
12134 root 20 0 6963564 26104 10532 S 0.0 0.2 0:00.00 java
12135 root 20 0 6963564 26104 10532 S 0.0 0.2 0:00.00 java
12136 root 20 0 6963564 26104 10532 S 0.0 0.2 0:00.00 java
12137 root 20 0 6963564 26104 10532 S 0.0 0.2 0:00.00 java
12138 root 20 0 6963564 26104 10532 S 0.0 0.2 0:00.00 java
12139 root 20 0 6963564 26104 10532 S 0.0 0.2 0:00.00 java
12140 root 20 0 6963564 26104 10532 S 0.0 0.2 0:00.03 java
2)cat /proc/pid/status | grep Thread
$ cat /proc/12121/status | grep Thread
Threads: 20
3)pstree -p pid:
$ pstree -p 12121
java(12121)─┬─{java}(12122)
├─{java}(12123)
├─{java}(12124)
├─{java}(12125)
├─{java}(12126)
├─{java}(12127)
├─{java}(12128)
├─{java}(12129)
├─{java}(12130)
├─{java}(12131)
├─{java}(12132)
├─{java}(12133)
├─{java}(12134)
├─{java}(12135)
├─{java}(12136)
├─{java}(12137)
├─{java}(12138)
├─{java}(12139)
└─{java}(12140)
注:上述三种方法得到的结果都是一致的。
2、通过java api打印java中的线程:
import java.lang.management.ManagementFactory;
import java.lang.management.ThreadInfo;
import java.lang.management.ThreadMXBean;
public class Test1 {
public static void main(String[] args) {
ThreadMXBean threadBean = ManagementFactory.getThreadMXBean();
ThreadInfo[] threadInfo = threadBean.dumpAllThreads(false, false);
for(ThreadInfo info : threadInfo){
System.out.println(info.getThreadId() + "--" + info.getThreadName() +"--"+ info.getThreadState().name());
}
}
}
输出结果:
4--Signal Dispatcher--RUNNABLE
3--Finalizer--WAITING
2--Reference Handler--WAITING
1--main--RUNNABLE
说明:虽然我们写了一个main函数的单线程程序,但是JVM会启动一些额外的线程,主要包括:
- Attach Listener 线程:负责接收外部JVM的命令,而对该命令进行执行的,并且把结果返回给发送者。如常用的命令:java -version、jmap、jstack等
- Signal Dispatcher 线程:Attach Listener线程接收到命令后,会交给Signal Dispatcher线程去进行分发到各个不同的模块处理命令,并且返回处理结果
- Finalizer 线程:在垃圾回收之前执行“对象完成”的Java系统线程
- Reference Handler 线程:处理引用对象本身(软引用、弱引用、虚引用)的垃圾回收问题
注意:Attach Listener、Signal Dispatcher线程在jvm启动的时候若没有初始化,那么则会在用户第一次执行jvm命令时启动这两个线程。
3、实战示例:
1)我们写一个main线程的(单线程)程序:
public class Test2 {
public static void main(String[] args) throws Exception {
while(true) {
Thread.sleep(2000);
System.out.println("main...");
}
}
}
2)运行后,使用上面的方法查看该java进程的线程数:
$ top -Hp 12121
top - 13:37:23 up 223 days, 18:41, 2 users, load average: 0.01, 0.02, 0.05
Threads: 20 total, 0 running, 20 sleeping, 0 stopped, 0 zombie
%Cpu(s): 0.1 us, 0.2 sy, 0.0 ni, 99.7 id, 0.0 wa, 0.0 hi, 0.0 si, 0.0 st
KiB Mem : 16267876 total, 549064 free, 3220720 used, 12498092 buff/cache
KiB Swap: 2097148 total, 2034684 free, 62464 used. 11924764 avail Mem
PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND
12121 root 20 0 6963564 26104 10532 S 0.0 0.2 0:00.00 java
12122 root 20 0 6963564 26104 10532 S 0.0 0.2 0:00.10 java
12123 root 20 0 6963564 26104 10532 S 0.0 0.2 0:00.00 java
12124 root 20 0 6963564 26104 10532 S 0.0 0.2 0:00.00 java
12125 root 20 0 6963564 26104 10532 S 0.0 0.2 0:00.00 java
12126 root 20 0 6963564 26104 10532 S 0.0 0.2 0:00.00 java
12127 root 20 0 6963564 26104 10532 S 0.0 0.2 0:00.00 java
12128 root 20 0 6963564 26104 10532 S 0.0 0.2 0:00.00 java
12129 root 20 0 6963564 26104 10532 S 0.0 0.2 0:00.00 java
12130 root 20 0 6963564 26104 10532 S 0.0 0.2 0:00.00 java
12131 root 20 0 6963564 26104 10532 S 0.0 0.2 0:00.00 java
12132 root 20 0 6963564 26104 10532 S 0.0 0.2 0:00.00 java
12133 root 20 0 6963564 26104 10532 S 0.0 0.2 0:00.00 java
12134 root 20 0 6963564 26104 10532 S 0.0 0.2 0:00.00 java
12135 root 20 0 6963564 26104 10532 S 0.0 0.2 0:00.00 java
12136 root 20 0 6963564 26104 10532 S 0.0 0.2 0:00.00 java
12137 root 20 0 6963564 26104 10532 S 0.0 0.2 0:00.00 java
12138 root 20 0 6963564 26104 10532 S 0.0 0.2 0:00.00 java
12139 root 20 0 6963564 26104 10532 S 0.0 0.2 0:00.00 java
12140 root 20 0 6963564 26104 10532 S 0.0 0.2 0:00.03 java
可以看到该java进程包含了20个线程(使用另外的两种方法得到同样的结果)。这里有个问题,为什么会有这么多线程呢?
3)使用jstack查看线程详情:
$ jstack -l 12121
2020-02-16 13:39:26
Full thread dump Java HotSpot(TM) 64-Bit Server VM (25.144-b01 mixed mode):
"Attach Listener" #10 daemon prio=9 os_prio=0 tid=0x00002b9ad4001000 nid=0x3c78 waiting on condition [0x0000000000000000]
java.lang.Thread.State: RUNNABLE
Locked ownable synchronizers:
- None
"Service Thread" #9 daemon prio=9 os_prio=0 tid=0x00002b9a800d2000 nid=0x2f6b runnable [0x0000000000000000]
java.lang.Thread.State: RUNNABLE
Locked ownable synchronizers:
- None
"C1 CompilerThread3" #8 daemon prio=9 os_prio=0 tid=0x00002b9a800c7000 nid=0x2f6a waiting on condition [0x0000000000000000]
java.lang.Thread.State: RUNNABLE
Locked ownable synchronizers:
- None
"C2 CompilerThread2" #7 daemon prio=9 os_prio=0 tid=0x00002b9a800c4800 nid=0x2f69 waiting on condition [0x0000000000000000]
java.lang.Thread.State: RUNNABLE
Locked ownable synchronizers:
- None
"C2 CompilerThread1" #6 daemon prio=9 os_prio=0 tid=0x00002b9a800c3000 nid=0x2f68 waiting on condition [0x0000000000000000]
java.lang.Thread.State: RUNNABLE
Locked ownable synchronizers:
- None
"C2 CompilerThread0" #5 daemon prio=9 os_prio=0 tid=0x00002b9a800c0000 nid=0x2f67 waiting on condition [0x0000000000000000]
java.lang.Thread.State: RUNNABLE
Locked ownable synchronizers:
- None
"Signal Dispatcher" #4 daemon prio=9 os_prio=0 tid=0x00002b9a800be800 nid=0x2f66 runnable [0x0000000000000000]
java.lang.Thread.State: RUNNABLE
Locked ownable synchronizers:
- None
"Finalizer" #3 daemon prio=8 os_prio=0 tid=0x00002b9a8008b800 nid=0x2f65 in Object.wait() [0x00002b9abd8be000]
java.lang.Thread.State: WAITING (on object monitor)
at java.lang.Object.wait(Native Method)
- waiting on <0x000000076d408ec8> (a java.lang.ref.ReferenceQueue$Lock)
at java.lang.ref.ReferenceQueue.remove(ReferenceQueue.java:143)
- locked <0x000000076d408ec8> (a java.lang.ref.ReferenceQueue$Lock)
at java.lang.ref.ReferenceQueue.remove(ReferenceQueue.java:164)
at java.lang.ref.Finalizer$FinalizerThread.run(Finalizer.java:209)
Locked ownable synchronizers:
- None
"Reference Handler" #2 daemon prio=10 os_prio=0 tid=0x00002b9a80086800 nid=0x2f64 in Object.wait() [0x00002b9abd7bd000]
java.lang.Thread.State: WAITING (on object monitor)
at java.lang.Object.wait(Native Method)
- waiting on <0x000000076d406b68> (a java.lang.ref.Reference$Lock)
at java.lang.Object.wait(Object.java:502)
at java.lang.ref.Reference.tryHandlePending(Reference.java:191)
- locked <0x000000076d406b68> (a java.lang.ref.Reference$Lock)
at java.lang.ref.Reference$ReferenceHandler.run(Reference.java:153)
Locked ownable synchronizers:
- None
"main" #1 prio=5 os_prio=0 tid=0x00002b9a80008800 nid=0x2f5a waiting on condition [0x00002b9a7b9c6000]
java.lang.Thread.State: TIMED_WAITING (sleeping)
at java.lang.Thread.sleep(Native Method)
at Test2.main(Test2.java:5)
Locked ownable synchronizers:
- None
"VM Thread" os_prio=0 tid=0x00002b9a8007f000 nid=0x2f63 runnable
"GC task thread#0 (ParallelGC)" os_prio=0 tid=0x00002b9a8001d800 nid=0x2f5b runnable
"GC task thread#1 (ParallelGC)" os_prio=0 tid=0x00002b9a8001f800 nid=0x2f5c runnable
"GC task thread#2 (ParallelGC)" os_prio=0 tid=0x00002b9a80021000 nid=0x2f5d runnable
"GC task thread#3 (ParallelGC)" os_prio=0 tid=0x00002b9a80023000 nid=0x2f5e runnable
"GC task thread#4 (ParallelGC)" os_prio=0 tid=0x00002b9a80025000 nid=0x2f5f runnable
"GC task thread#5 (ParallelGC)" os_prio=0 tid=0x00002b9a80026800 nid=0x2f60 runnable
"GC task thread#6 (ParallelGC)" os_prio=0 tid=0x00002b9a80028800 nid=0x2f61 runnable
"GC task thread#7 (ParallelGC)" os_prio=0 tid=0x00002b9a8002a800 nid=0x2f62 runnable
"VM Periodic Task Thread" os_prio=0 tid=0x00002b9a800d5000 nid=0x2f6c waiting on condition
JNI global references: 6
在thread dump中每个线程都有一个nid(十六进制),该进成共有20个nid(线程)。主要包括:
- Attach Listener、Signal Dispatcher、Finalizer、Reference Handler
- GC task thread:gc线程
- C1 CompilerThread:C1即时编译线程;
- C2 CompilerThread:C2即时编译线程;
- main:用户线程
4)修改java代码,添加一个线程:
public class Test {
public static void main(String[] args) throws Exception {
System.out.println("main...");
new Thread() {
public void run() {
while(true) {
try {
Thread.sleep(1000);
System.out.println("test1...");
} catch(Exception e) {
}
}
}
}.start();
new Thread() {
public void run() {
while(true) {
try {
Thread.sleep(1000);
System.out.println("test2...");
} catch(Exception e) {
}
}
}
}.start();
}
}
一共起了有3个线程(算main线程)。接下来我们运行该程序,并使用top查看该进程中的线程数。
$ top -Hp 11444
top - 14:05:29 up 223 days, 19:09, 2 users, load average: 0.00, 0.01, 0.05
Threads: 22 total, 0 running, 22 sleeping, 0 stopped, 0 zombie
%Cpu(s): 0.0 us, 0.1 sy, 0.0 ni, 99.9 id, 0.0 wa, 0.0 hi, 0.0 si, 0.0 st
KiB Mem : 16267876 total, 537252 free, 3222404 used, 12508220 buff/cache
KiB Swap: 2097148 total, 2034684 free, 62464 used. 11914756 avail Mem
PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND
11444 root 20 0 7096692 26292 10624 S 0.0 0.2 0:00.00 java
11445 root 20 0 7096692 26292 10624 S 0.0 0.2 0:00.09 java
11447 root 20 0 7096692 26292 10624 S 0.0 0.2 0:00.00 java
11448 root 20 0 7096692 26292 10624 S 0.0 0.2 0:00.00 java
11449 root 20 0 7096692 26292 10624 S 0.0 0.2 0:00.00 java
11450 root 20 0 7096692 26292 10624 S 0.0 0.2 0:00.00 java
11451 root 20 0 7096692 26292 10624 S 0.0 0.2 0:00.00 java
11452 root 20 0 7096692 26292 10624 S 0.0 0.2 0:00.00 java
11453 root 20 0 7096692 26292 10624 S 0.0 0.2 0:00.00 java
11454 root 20 0 7096692 26292 10624 S 0.0 0.2 0:00.00 java
11466 root 20 0 7096692 26292 10624 S 0.0 0.2 0:00.00 java
11473 root 20 0 7096692 26292 10624 S 0.0 0.2 0:00.00 java
11477 root 20 0 7096692 26292 10624 S 0.0 0.2 0:00.00 java
11487 root 20 0 7096692 26292 10624 S 0.0 0.2 0:00.00 java
11488 root 20 0 7096692 26292 10624 S 0.0 0.2 0:00.00 java
11489 root 20 0 7096692 26292 10624 S 0.0 0.2 0:00.00 java
11490 root 20 0 7096692 26292 10624 S 0.0 0.2 0:00.00 java
11491 root 20 0 7096692 26292 10624 S 0.0 0.2 0:00.00 java
11492 root 20 0 7096692 26292 10624 S 0.0 0.2 0:00.00 java
11493 root 20 0 7096692 26292 10624 S 0.0 0.2 0:00.00 java
11494 root 20 0 7096692 26292 10624 S 0.0 0.2 0:00.00 java
11495 root 20 0 7096692 26292 10624 S 0.0 0.2 0:00.00 java
我们看到一共有22个线程,比之前的单线程程序多了两个我们自定义的线程。使用jstack查看可以发现多了如下两个线程信息:
"Thread-1" #11 prio=5 os_prio=0 tid=0x00002adca410a000 nid=0x2ce7 waiting on condition [0x00002adce1715000]
java.lang.Thread.State: TIMED_WAITING (sleeping)
at java.lang.Thread.sleep(Native Method)
at Test$2.run(Test.java:24)
Locked ownable synchronizers:
- None
"Thread-0" #10 prio=5 os_prio=0 tid=0x00002adca4108000 nid=0x2ce6 waiting on condition [0x00002adce1614000]
java.lang.Thread.State: TIMED_WAITING (sleeping)
at java.lang.Thread.sleep(Native Method)
at Test$1.run(Test.java:12)
Locked ownable synchronizers:
- None