import java.lang.management.ManagementFactory;
import java.lang.management.ThreadInfo;
import java.lang.management.ThreadMXBean;
import java.util.Scanner;
public class HelloWorld {
public static void main(String[] args) {
System.out.println("---------------main方法执行----------------------");
// 获取线程管理MXBean
ThreadMXBean threadMXBean = ManagementFactory.getThreadMXBean();
// 无需获取同步的monitor和synchronizer信息
// 仅获取线程和线程堆栈信息
ThreadInfo[] threadInfos = threadMXBean.dumpAllThreads(false, false);
System.out.println("-------------开始打印线程信息------------");
for (ThreadInfo threadInfo : threadInfos) {
// 获取线程id和线程名
System.out.println("[" + threadInfo.getThreadId() + "]" + "------->" + threadInfo.getThreadName());
}
System.out.println("main方法执行完毕");
Scanner scanner = new Scanner(System.in);
scanner.next();
}
}
---------------main方法执行------------------
-------------开始打印线程信息-----------------
[6]------->Monitor Ctrl-Break
[5]------->Attach Listener
[4]------->Signal Dispatcher
[3]------->Finalizer
[2]------->Reference Handler
[1]------->main
main方法执行完毕
结果分析
可见,一个简单的main()方法,JVM还为我们创建了除main线程之外的5个线程.(创建的线程数和JDK版本以及IDE有关,这里是JDK1.7,Idea)
Monitor Ctrl-Break
:使用Idea Run程序会Fork出这个线程,Eclipse不会.
Attach Listener
:负责接收到外部的命令,对该命令进行执行并把结果返回给发送者。
Signal Dispatcher
:当Attach Listener把命令接收成功后,会交给signal dispather线程去进行分发到各个不同的模块处理命令,并且返回处理结果。
Finalizer
:这个线程是在main线程之后创建的,其优先级为10,主要用于在垃圾收集前,调用对象的finalize()方法.有4点需要注意:
Reference Handler
: JVM在创建main线程后就创建Reference Handler线程,其优先级最高,为10,它主要用于处理引用对象本身(软引用、弱引用、虚引用)的垃圾回收问题。
两种线程
java有两种线程,守护线程和非守护线程(用户线程).
查看线程属性
以刚才的main()方法为例,使用JDK自带的工具查看线程属性
jps [options] [hostid]
,hostid参数为空默认访问本地的虚拟机.options参数也可为空,常用参数有-l,显示全路径名的进程.$ jps
448336 Launcher
398940
453576 Launcher
454576 HelloWorld
452796 Jps
$ jps -l
448336 org.jetbrains.jps.cmdline.Launcher
398940
451576 sun.tools.jps.Jps
453576 org.jetbrains.jps.cmdline.Launcher
454576 com.cxw.HelloWorld
找到HelloWorld对应的pid,再使用jstack工具即可查看对应线程的堆栈信息
// 命令:jstack pid
$ jstack 454576
控制台打印信息如下
// 线程名后带daemon的就是守护线程
"Monitor Ctrl-Break" daemon prio=6 tid=0x000000000e4b8800 nid=0x6f198 runnable [0x000000000fade000]
java.lang.Thread.State: RUNNABLE
at java.net.SocketInputStream.socketRead0(Native Method)
at java.net.SocketInputStream.read(SocketInputStream.java:150)
at java.net.SocketInputStream.read(SocketInputStream.java:121)
at sun.nio.cs.StreamDecoder.readBytes(StreamDecoder.java:283)
at sun.nio.cs.StreamDecoder.implRead(StreamDecoder.java:325)
at sun.nio.cs.StreamDecoder.read(StreamDecoder.java:177)
- locked <0x00000000d602bed0> (a java.io.InputStreamReader)
at java.io.InputStreamReader.read(InputStreamReader.java:184)
at java.io.BufferedReader.fill(BufferedReader.java:154)
at java.io.BufferedReader.readLine(BufferedReader.java:317)
- locked <0x00000000d602bed0> (a java.io.InputStreamReader)
at java.io.BufferedReader.readLine(BufferedReader.java:382)
at com.intellij.rt.execution.application.AppMainV2$1.run(AppMainV2.java:64)
// 守护线程Attach Listener
"Attach Listener" daemon prio=10 tid=0x000000000e440800 nid=0x6d85c waiting on condition [0x0000000000000000]
java.lang.Thread.State: RUNNABLE
// 守护线程Signal Dispatcher
"Signal Dispatcher" daemon prio=10 tid=0x000000000e43d000 nid=0x6f208 runnable [0x0000000000000000]
java.lang.Thread.State: RUNNABLE
// 守护线程Finalizer
"Finalizer" daemon prio=8 tid=0x000000000e3b9800 nid=0x6d1d4 in Object.wait() [0x000000000f7df000]
java.lang.Thread.State: WAITING (on object monitor)
at java.lang.Object.wait(Native Method)
- waiting on <0x00000000d5eb57f0> (a java.lang.ref.ReferenceQueue$Lock)
at java.lang.ref.ReferenceQueue.remove(ReferenceQueue.java:135)
- locked <0x00000000d5eb57f0> (a java.lang.ref.ReferenceQueue$Lock)
at java.lang.ref.ReferenceQueue.remove(ReferenceQueue.java:151)
at java.lang.ref.Finalizer$FinalizerThread.run(Finalizer.java:177)
// 守护线程Reference Handler
"Reference Handler" daemon prio=10 tid=0x000000000e3b0800 nid=0x6a3a0 in Object.wait() [0x000000000f6df000]
java.lang.Thread.State: WAITING (on object monitor)
at java.lang.Object.wait(Native Method)
- waiting on <0x00000000d5eb5370> (a java.lang.ref.Reference$Lock)
at java.lang.Object.wait(Object.java:503)
at java.lang.ref.Reference$ReferenceHandler.run(Reference.java:133)
- locked <0x00000000d5eb5370> (a java.lang.ref.Reference$Lock)
// 用户线程main
"main" prio=6 tid=0x000000000542e000 nid=0x6de30 runnable [0x000000000370f000]
java.lang.Thread.State: RUNNABLE
at java.io.FileInputStream.readBytes(Native Method)
at java.io.FileInputStream.read(FileInputStream.java:242)
at java.io.BufferedInputStream.read1(BufferedInputStream.java:273)
at java.io.BufferedInputStream.read(BufferedInputStream.java:334)
- locked <0x00000000d5f07f30> (a java.io.BufferedInputStream)
at sun.nio.cs.StreamDecoder.readBytes(StreamDecoder.java:283)
at sun.nio.cs.StreamDecoder.implRead(StreamDecoder.java:325)
at sun.nio.cs.StreamDecoder.read(StreamDecoder.java:177)
- locked <0x00000000d5fedcd8> (a java.io.InputStreamReader)
at java.io.InputStreamReader.read(InputStreamReader.java:184)
at java.io.Reader.read(Reader.java:100)
at java.util.Scanner.readInput(Scanner.java:849)
at java.util.Scanner.next(Scanner.java:1414)
at com.cxw.HelloWorld.main(HelloWorld.java:24)
public class DaemonThreadDemo {
public class MyDaemon implements Runnable {
@Override
public void run() {
for (int i = 0; i < 5; i++) {
System.out.println("线程第"+i+"次执行");
}
}
}
/**
* 创建守护线程
* @throws IOException
*/
@Test
public void DaemonThreadTest() throws IOException {
Thread thread = new Thread(new MyDaemon());
// setDaemon(true)设置线程为守护线程
thread.setDaemon(true);
thread.start();
// isDaemon()方法查看当前线程是不是守护线程
if (thread.isDaemon()) {
System.out.println("当前执行的是守护线程");
} else {
System.out.println("当前执行的是非守护线程");
}
System.in.read();
}
}
执行结果
当前执行的是守护线程
线程第0次执行
线程第1次执行
线程第2次执行
线程第3次执行
线程第4次执行
在线程执行前,使用setDaemon()方法,将线程设置为守护线程.
public class DaemonThreadDemo {
public class MyDaemon implements Runnable {
@Override
public void run() {
for (int i = 0; i < 5; i++) {
System.out.println("线程第" + i + "次执行");
}
Thread subThread = new Thread(new Runnable() {
@Override
public void run() {
// 守护线程中fork出的子线程依然是守护线程
System.out.println("子线程执行,当前线程是否守护线程" + Thread.currentThread().isDaemon());
}
});
subThread.start();
}
}
/**
* 创建守护线程
*
* @throws IOException
*/
@Test
public void DaemonThreadTest() throws IOException {
Thread thread = new Thread(new MyDaemon());
// setDaemon(true)设置线程为守护线程
thread.setDaemon(true);
thread.start();
// isDaemon()方法查看当前线程是不是守护线程
if (thread.isDaemon()) {
System.out.println("当前执行的是守护线程");
} else {
System.out.println("当前执行的是非守护线程");
}
System.in.read();
}
}
执行结果
当前执行的是守护线程
线程第0次执行
线程第1次执行
线程第2次执行
线程第3次执行
线程第4次执行
子线程执行,当前线程是否守护线程true
可以看到,在守护线程中fork出的子线程依然是守护线程.
public class DaemonThreadDemo {
@Test
public void ThreadDemo() {
Thread thread = new Thread(new Runnable() {
@Override
public void run() {
System.out.println("线程执行,当前线程是否是守护线程"+Thread.currentThread().isDaemon());
}
});
thread.start();
}
}
执行结果
线程执行,当前线程是否是守护线程false
可以明显看出,在main()方法,以及Junit的Test方法中,没有使用setDaemon()方法设置过的线程和main()方法,Test方法保持相同的线程属性,即非守护线程.
文章参考:
JVM 内部运行线程介绍