(一)java启动后拥有的线程
我们首先看代码,打印普通main方法执行时,程序到底有几条线程,线程又是怎样的。
public class ThreadPrint {
public static void main(String[] args) throws JsonProcessingException, InterruptedException {
// 获取Java线程管理MXBean
ThreadMXBean threadMXBean = ManagementFactory.getThreadMXBean();
// 不需要获取同步的monitor和synchronizer信息,仅获取线程和线程堆栈信息
ThreadInfo[] threadInfos = threadMXBean.dumpAllThreads(false, false);
// 遍历线程信息,仅打印线程ID和线程名称信息
for (ThreadInfo threadInfo : threadInfos) {
System.out.println("[" + threadInfo.getThreadId() + "] " + threadInfo.
getThreadName());
}
Thread.sleep(1000000);
}
}
输出结果如下:
下面分别介绍一下正在运行的线程:
-
[6] Monitor Ctrl-Break
跟JVM 关系不大,他是 IDEA 通过反射的方式,开启一个随着我们运行的jvm进程开启与关闭的一个监听线程。 复制代码
-
[5] Attach Listener
附加侦听器,简单来说,他是 jdk 里边一个工具类提供的jvm进程之间通信的工具。 举例来说,比如在cmd中输入 java -version,jvm中jstat、jstack、jmap、dump等。 复制代码
如果自己电脑没有打印这一行,可以尝试开启我们这个线程的两个方式:
- 通过 vm,参数开启。-XX: StartAttachListener
- 2.延迟开启: md --java -version --> JVM 实时开启Attach Listener线程
-
[4] Signal Dispatcher
信号分发器。我们通过 cmd 发送jstack,传到了 jvm 进程,这时候信号分发器就要发挥作用了。 复制代码
-
[3] Finalizer
JVM 垃圾回收相关的内容。此处只做简单的介绍。 1. 只有当开始一轮垃圾收集的时候,才会开始调用finalize方法。 2. daemon prio=10 高优先级的守护线程。 3. jvm在垃圾收集的时候,会将失去引用的对象封装到我们的 Fianlizer 对象(Reference), 放入我们的 F-queue 队列中。由 Finalizer 线程执行inalize方法 复制代码
-
[2] Reference Handler
引用处理的线程。强,软,弱,虚。 -GC 有不同表现 - JVM深入分析 复制代码
-
[1] main
就是我们运行的main线程 复制代码
(二)查看线程级别
1.可以在idea工具Terminal中输入jps查看进程的pid,我们可以看到ThreadPrint的pid是37720
2.再通过jstack pid命令可以查看到进程日志
其中我们把需要了解的部分拿出来 daemon表示守护线程 prio表示jvm中的优先级 os_prio表示线程在操作系统中所占的优先级
"Monitor Ctrl-Break" #6 daemon prio=5 os_prio=0 tid=0x0000000018e04800 nid=0xc918 runnable [0x000000001967f000]
Monitor Ctrl-Break是在idea中特有的线程
"Attach Listener" #5 daemon prio=5 os_prio=2 tid=0x00000000171eb000 nid=0x9230 waiting on condition [0x0000000000000000]
prio=5 延迟开启的问题
"Signal Dispatcher" #4 daemon prio=9 os_prio=2 tid=0x00000000171d3800 nid=0x9054 runnable [0x0000000000000000]
"Finalizer" #3 daemon prio=8 os_prio=1 tid=0x00000000171c6800 nid=0xc26c in Object.wait() [0x0000000018b1e000]
(Finalizer 专注垃圾收集,垃圾收集 -- 并行收集,不阻碍用户线程,低优先级线程。 prio=8 他是一个守护线程啊。而且这个线程目前并没有真正的开启,不足以发生minorgc或者是 full gc)
"Reference Handler" #2 daemon prio=10 os_prio=2 tid=0x000000001575f000 nid=0x9a38 in Object.wait() [0x0000000018a1f000]
(引用处理线程-GC相关线程:GC 很重要,优先级高)
"main" #1 prio=5 os_prio=0 tid=0x0000000003624000 nid=0xaba0 waiting on condition [0x000000000351f000]
(操作系统面向的是JVM 进程,JVM 进程里面向的是 我们的main函数,。所以对于我们的操作系统如何看待我们的main函数优先级,都无关紧要。 只要os 给我们jvm进程足够公平的优先级就行。)
复制代码
(三)总结
在一个进程里可以创建多个线程,这些线程都拥有各自的计数器、堆栈和局部变量等属性,并且能够访问共享的内存变量。处理器在这些线程上高速切换,让使用者感觉到这些线程在同时执行。
当前的main函数就是一个 JVM 进程。 打印出来的6条线程信息就是进程中的多条线程。