两个问题
  1. 什么是守护线程?守护线程与非守护线程有什么区别?其应用场景有哪些?
  2. 一个简单的Java程序,启动后JVM创建了哪些线程,它们的作用是什么?

熟悉上面两个问题的同学可以绕过了,不太熟的同学可以继续往下看,哈哈!

守护线程

守护线程,又叫Daemon线程,它有以下几个特点:

  1. 守护线程通常由虚拟机自己使用,比如垃圾收集器的线程;
  2. Java程序可以把它任何创建的线程标记为守护线程;但必须在线程运行前设置
  3. Java初始线程(即开始于main方法的线程)是非守护线程;
  4. 只要还有任何非守护线程在运行,那么这个Java程序也在运行,即这个JVM实例还存活着;当JVM中的所有非守护线程都终止时,JVM实例将自动退出;

看下面这个例子:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
public class DaemonTest {
 
     public static void main(String[] args) throws InterruptedException,
             IOException {
         Thread daemon = new Thread( new DaemonThread());
         daemon.setName( "My Daemon Thread" );
         daemon.setDaemon( true );
         daemon.start();
 
         for ( int i = 0 ; i < 10 ; i++) {
             System.out.println( "Main thread: " + i);
             Thread.sleep( 1000 );
         }
     }
}
 
class DaemonThread implements Runnable {
     private int index = 0 ;
 
     @Override
     public void run() {
         while (index < 100 ) {
             System.out.println( "========= Daemon thread: " + index++);
             try {
                 Thread.sleep( 1000 );
             } catch (InterruptedException e) {
                 e.printStackTrace();
             }
         }
     }
}

主线程在创建一个Daemon线程后,循环10次结束;此时JVM中没有任何非Daemon线程,因此JVM将自动退出,其结果是导致创建的Daemon线程没有执行完既定任务就被迫终止了;程序结果如下:

1
2
3
4
5
6
7
8
Main thread: 0
========= Daemon thread: 0
========= Daemon thread: 1
Main thread: 1
Main thread: 2
......
Main thread: 9
========= Daemon thread: 10

结论:不要将业务逻辑封装在Daemon线程中执行,否则结果会不稳定;

JVM中的线程

上面例子中,虚拟机在启动后,一共创建了多少个线程呢?不用猜,我们通过程序或工具来看;

1、通过程序

代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
public class JVMThreadTest {
 
     public static void main(String[] args) {
         Thread[] ts = getAllThread();
         for (Thread t : ts) {
             System.out.println(t.getId() + ": " + t.getName() + ", Priority: "
                     + t.getPriority());
         }
     }
 
     public static Thread[] getAllThread() {
         ThreadGroup root = Thread.currentThread().getThreadGroup();
         ThreadGroup ttg = root;
 
         while ((ttg = ttg.getParent()) != null ) {
             root = ttg;
         }
 
         Thread[] tlist = new Thread[( int ) (root.activeCount() * 1.2 )];
         return java.util.Arrays.copyOf(tlist, root.enumerate(tlist, true ));
     }
}

上面的代码先取得所有的线程,然后依次打印其线程Id、线程名和优先级,结果如下:

1
2
3
4
5
2 : Reference Handler, Priority: 10
3 : Finalizer, Priority: 8
4 : Signal Dispatcher, Priority: 9
5 : Attach Listener, Priority: 5
1 : main, Priority: 5

2、通过jstack

通过jstack -l pid,可以得到如下栈信息:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
"My Daemon Thread" daemon prio= 6 tid= 0x01e82400 nid= 0x1740 waiting on condition
[ 0x0414f000 ]
    java.lang.Thread.State: TIMED_WAITING (sleeping)
         at java.lang.Thread.sleep(Native Method)
         at inside.jvm.daemon.DaemonThread.run(DaemonTest.java: 34 )
         at java.lang.Thread.run(Unknown Source)
 
    Locked ownable synchronizers:
         - None
 
"Low Memory Detector" daemon prio= 6 tid= 0x01e50400 nid= 0xc78 runnable [ 0x0000000
0 ]
    java.lang.Thread.State: RUNNABLE
 
    Locked ownable synchronizers:
         - None
 
"C1 CompilerThread0" daemon prio= 10 tid= 0x01e4dc00 nid= 0x410 waiting on conditio
n [ 0x00000000 ]
    java.lang.Thread.State: RUNNABLE
 
    Locked ownable synchronizers:
         - None
 
"Attach Listener" daemon prio= 10 tid= 0x01e4a800 nid= 0x109c waiting on condition
[ 0x00000000 ]
    java.lang.Thread.State: RUNNABLE
 
    Locked ownable synchronizers:
         - None
 
"Signal Dispatcher" daemon prio= 10 tid= 0x01e47800 nid= 0x9b0 runnable [ 0x00000000
]
    java.lang.Thread.State: RUNNABLE
 
    Locked ownable synchronizers:
         - None
 
"Finalizer" daemon prio= 8 tid= 0x01e40400 nid= 0x17d0 in Object.wait() [ 0x03f6f000
]
    java.lang.Thread.State: WAITING (on object monitor)
         at java.lang.Object.wait(Native Method)
         - waiting on < 0x23d51148 > (a java.lang.ref.ReferenceQueue$Lock)
         at java.lang.ref.ReferenceQueue.remove(Unknown Source)
         - locked < 0x23d51148 > (a java.lang.ref.ReferenceQueue$Lock)
         at java.lang.ref.ReferenceQueue.remove(Unknown Source)
         at java.lang.ref.Finalizer$FinalizerThread.run(Unknown Source)
 
    Locked ownable synchronizers:
         - None
 
"Reference Handler" daemon prio= 10 tid= 0x01e3b800 nid= 0x1064 in Object.wait() [ 0
x03f1f000]
    java.lang.Thread.State: WAITING (on object monitor)
         at java.lang.Object.wait(Native Method)
         - waiting on < 0x23d51048 > (a java.lang.ref.Reference$Lock)
         at java.lang.Object.wait(Object.java: 485 )
         at java.lang.ref.Reference$ReferenceHandler.run(Unknown Source)
         - locked < 0x23d51048 > (a java.lang.ref.Reference$Lock)
 
    Locked ownable synchronizers:
         - None
 
"main" prio= 6 tid= 0x014f9400 nid= 0x1154 waiting on condition [ 0x001ff000 ]
    java.lang.Thread.State: TIMED_WAITING (sleeping)
         at java.lang.Thread.sleep(Native Method)
         at inside.jvm.daemon.DaemonTest.main(DaemonTest.java: 21 )
 
    Locked ownable synchronizers:
         - None
 
"VM Thread" prio= 10 tid= 0x01dffc00 nid= 0xab8 runnable
 
"VM Periodic Task Thread" prio= 10 tid= 0x01e6f400 nid= 0xcd8 waiting on condition

3、通过jconsole
通过JDK自带的JConsole也可以查看JVM中的线程,如下图:
JVM Threads

各线程介绍
  1. main线程

    这个不用多解释了,主线程,JVM创建的第一个线程,属于非daemon线程;从jstack的结果可以看出,main线程通过Thread.sleep()而进入TIMED_WAITING状态;

  2. Reference Handler线程

    JVM在创建main线程后就创建Reference Handler线程,其优先级最高,为10,它主要用于处理引用对象本身(软引用、弱引用、虚引用)的垃圾回收问题;关于引用,可以参考Java:对象的强、软、弱、虚引用这篇blog;

  3. Finalizer线程

    这个线程也是在main线程之后创建的,其优先级为8,主要用于在垃圾收集前,调用对象的finalize()方法;关于Finalizer线程的几点:
    1) 只有当开始一轮垃圾收集时,才会开始调用finalize()方法;因此并不是所有对象的finalize()方法都会被执行;
    2) 该线程也是daemon线程,因此如果虚拟机中没有其他非daemon线程,不管该线程有没有执行完finalize()方法,JVM也会退出;
    3) JVM在垃圾收集时会将失去引用的对象包装成Finalizer对象(Reference的实现),并放入ReferenceQueue,由Finalizer线程来处理;最后将该Finalizer对象的引用置为null,由垃圾收集器来回收;
    4) JVM为什么要单独用一个线程来执行finalize()方法呢?如果JVM的垃圾收集线程自己来做,很有可能由于在finalize()方法中误操作导致GC线程停止或不可控,这对GC线程来说是一种灾难;

  4. Signal Dispatcher线程

    该线程用于处理操作系统发送给的JVM信号;关于JVM信号处理可以参考Revelations on Java signal handling and termination这篇blog;

  5. My Daemon Thread线程

    这个就是在main线程里创建出来的daemon线程;

  6. Attach Listener线程

    这个线程暂时还查不到相关的资料,了解的同学请跟贴回复一下,谢谢!

  7. Low Memory Detector线程

    网上查资料,说这个线程是负责对可使用内存进行检测,如果发现可用内存低,分配新的内存空间;我有两个疑问:
    1) 在哪里分配新的内存?
    2) 这个线程什么时候被创建的?代码在哪?

  8. C1 CompilerThread0线程

    用来调用JITing,实时编译装卸class;

  9. VM Thread、VM Periodic Task Thread线程

    关于上面三个线程,我同样存在以上两个疑问;

Logo

华为开发者空间,是为全球开发者打造的专属开发空间,汇聚了华为优质开发资源及工具,致力于让每一位开发者拥有一台云主机,基于华为根生态开发、创新。

更多推荐