youyichannel

志于道,据于德,依于仁,游于艺!

0%

JUC初探-01

获取所有的Java线程

示例代码:

import java.lang.management.ManagementFactory;
import java.lang.management.ThreadInfo;
import java.lang.management.ThreadMXBean;

/**
* 添加JVM参数 -XX:+StartAttachListener
* @author codejuzi
*/
public class ThreadPrint {
public static void main(String[] args) {
// 获取Java线程管理Bean
ThreadMXBean threadMXBean = ManagementFactory.getThreadMXBean();
// 不需要获取同步的monitor和synchronizer信息,仅获取线程和线程堆栈信息
ThreadInfo[] threadInfos = threadMXBean.dumpAllThreads(false, false);
// 遍历线程信息,仅打印线程ID + 线程名称信息
for (ThreadInfo threadInfo : threadInfos) {
System.out.println("[" + threadInfo.getThreadId() + "]" + threadInfo.getThreadName());
}
}
}

输出结果:

[6] Monitor Ctrl-Break
[5] Attach Listener
[4] Signal Dispatcher
[3] Finalizer
[2] Reference Handler
[1] main

输出6条线程 => Java天生就是多线程。

一个进程中可以创建多个线程,这些线程都拥有各自的计数器、堆栈和局部变量等属性,并且能够访问共享的内存变量。处理器在这些线程上高速切换,让使用者感觉到这些线程在同时执行。

结果解析:

[1] main :主线程

[2] Reference Handler:引用处理的线程,Java中有强软弱虚四种引用,在GC时有不同的表现

[3] Finalizer

  1. JVM垃圾回收相关的内容,只有当开始一轮GC的时候,才会开启调用finalize()方法
  2. 高优先级的守护线程(之后会叙述)
  3. JVM在垃圾回收的时候,它会将失去引用的对象封装到Finalizer对象,放入F-queue队列中,再由Finalizer线程执行finalize()方法

[4] Signal Dispatcher:信号分发器,我们通过终端发送jstack,传送到JVM进程,信号分发器在此时发挥作用

[5] Attach Listener:附加监听器,是JDK中的一个工具类,提供JVM进程之间通信的工具,比如终端中的java --version命令和JVM中的jstack进程间的通信。

开启线程的两个方式:

  1. 通过JVM参数开启,-XX:+StartAttachListener
  2. 延迟开启,比如在终端输入java --version,那么JVM在此时就会开启线程,即适时开启Attach Listener线程

[6] Monitor Ctrl-Break:跟JVM关系不大,它是IDEA通过反射的方式,开启一个随着我们运行的JVM进程开启和关闭的一个监听线程

线程日志

通过jps + jstack命令来查看线程日志

"Monitor Ctrl-Break" #6 daemon prio=5 os_prio=31 tid=0x00000001511af000 nid=0x4903 runnable [0x0000000170f0a000]
java.lang.Thread.State: RUNNABLE
at java.net.SocketInputStream.socketRead0(Native Method)
at java.net.SocketInputStream.socketRead(SocketInputStream.java:116)
at java.net.SocketInputStream.read(SocketInputStream.java:171)
at java.net.SocketInputStream.read(SocketInputStream.java:141)
at sun.nio.cs.StreamDecoder.readBytes(StreamDecoder.java:284)
at sun.nio.cs.StreamDecoder.implRead(StreamDecoder.java:326)
at sun.nio.cs.StreamDecoder.read(StreamDecoder.java:178)
- locked <0x000000076adf03b8> (a java.io.InputStreamReader)
at java.io.InputStreamReader.read(InputStreamReader.java:184)
at java.io.BufferedReader.fill(BufferedReader.java:161)
at java.io.BufferedReader.readLine(BufferedReader.java:324)
- locked <0x000000076adf03b8> (a java.io.InputStreamReader)
at java.io.BufferedReader.readLine(BufferedReader.java:389)
at com.intellij.rt.execution.application.AppMainV2$1.run(AppMainV2.java:53)

"Attach Listener" #5 daemon prio=5 os_prio=31 tid=0x000000014d02d000 nid=0x4703 waiting on condition [0x0000000000000000]
java.lang.Thread.State: RUNNABLE

"Signal Dispatcher" #4 daemon prio=9 os_prio=31 tid=0x000000014d02c000 nid=0x4603 runnable [0x0000000000000000]
java.lang.Thread.State: RUNNABLE

"Finalizer" #3 daemon prio=8 os_prio=31 tid=0x000000015081e800 nid=0x3503 in Object.wait() [0x00000001707ce000]
java.lang.Thread.State: WAITING (on object monitor)
at java.lang.Object.wait(Native Method)
- waiting on <0x000000076ab09528> (a java.lang.ref.ReferenceQueue$Lock)
at java.lang.ref.ReferenceQueue.remove(ReferenceQueue.java:144)
- locked <0x000000076ab09528> (a java.lang.ref.ReferenceQueue$Lock)
at java.lang.ref.ReferenceQueue.remove(ReferenceQueue.java:165)
at java.lang.ref.Finalizer$FinalizerThread.run(Finalizer.java:188)

"Reference Handler" #2 daemon prio=10 os_prio=31 tid=0x000000015081b800 nid=0x3203 in Object.wait() [0x00000001705c2000]
java.lang.Thread.State: WAITING (on object monitor)
at java.lang.Object.wait(Native Method)
- waiting on <0x000000076ab070a8> (a java.lang.ref.Reference$Lock)
at java.lang.Object.wait(Object.java:502)
at java.lang.ref.Reference.tryHandlePending(Reference.java:191)
- locked <0x000000076ab070a8> (a java.lang.ref.Reference$Lock)
at java.lang.ref.Reference$ReferenceHandler.run(Reference.java:153)

"main" #1 prio=5 os_prio=31 tid=0x000000015080b000 nid=0x1303 waiting on condition [0x000000016f14a000]
java.lang.Thread.State: TIMED_WAITING (sleeping)
at java.lang.Thread.sleep(Native Method)
at com.juzi.juc.ThreadPrint.main(ThreadPrint.java:22)

main线程的os_prio=31,操作系统进程优先级。说明:操作系统面向的是JVM进程,JVM进程面向的是Java中的main函数,所以对于操作系统如何看待我们的main函数的优先级,这个是没有关系的,只要操作系统给我们JVM进程足够公平的优先级就行。

Reference Handler (prio=10):GC相关线程。

Finalizer (daemon, prio=8):专注垃圾回收,垃圾收集趋向于并行收集,不阻碍用户线程,低优先级线程。其中优先级为8,不代表它的优先级很高,一方面它是一个守护线程,另一方面就是这个线程目前并没有真正的启动,因为目前还不足以发生MinorGC或者是FullGC。

Attach Listener (prio=5):存在延迟开启的问题,不需要很高的优先级

资料:https://www.bilibili.com/video/BV1fC4y1A7vj/ 强推 河北王校长