天天看点

JVM线程知多少

本文已在黄金档上发表,原文链接:http://www.goldendoc.org/2011/11/jvm-thread/

两个问题

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

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

守护线程

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

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

看下面这个例子:

1

public

class

DaemonTest {

2

3

public

static

void

main(String[] args) 

throws

InterruptedException,

4

IOException {

5

Thread daemon = 

new

Thread(

new

DaemonThread());

6

daemon.setName(

"My Daemon Thread"

);

7

daemon.setDaemon(

true

);

8

daemon.start();

9

10

for

(

int

i = 

; i < 

10

; i++) {

11

System.out.println(

"Main thread: "

+ i);

12

Thread.sleep(

1000

);

13

}

14

}

15

}

16

17

class

DaemonThread 

implements

Runnable {

18

private

int

index = 

;

19

20

@Override

21

public

void

run() {

22

while

(index < 

100

) {

23

System.out.println(

"========= Daemon thread: "

+ index++);

24

try

{

25

Thread.sleep(

1000

);

26

catch

(InterruptedException e) {

27

e.printStackTrace();

28

}

29

}

30

}

31

}

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

1

Main thread: 

2

========= Daemon thread: 

3

========= Daemon thread: 

1

4

Main thread: 

1

5

Main thread: 

2

6

......

7

Main thread: 

9

8

========= Daemon thread: 

10

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

JVM中的线程

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

1、通过程序

代码如下:

1

public

class

JVMThreadTest {

2

3

public

static

void

main(String[] args) {

4

Thread[] ts = getAllThread();

5

for

(Thread t : ts) {

6

System.out.println(t.getId() + 

": "

+ t.getName() + 

", Priority: "

7

+ t.getPriority());

8

}

9

}

10

11

public

static

Thread[] getAllThread() {

12

ThreadGroup root = Thread.currentThread().getThreadGroup();

13

ThreadGroup ttg = root;

14

15

while

((ttg = ttg.getParent()) != 

null

) {

16

root = ttg;

17

}

18

19

Thread[] tlist = 

new

Thread[(

int

) (root.activeCount() * 

1.2

)];

20

return

java.util.Arrays.copyOf(tlist, root.enumerate(tlist, 

true

));

21

}

22

}

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

1

2

: Reference Handler, Priority: 

10

2

3

: Finalizer, Priority: 

8

3

4

: Signal Dispatcher, Priority: 

9

4

5

: Attach Listener, Priority: 

5

5

1

: main, Priority: 

5

2、通过jstack

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

1

"My Daemon Thread"

daemon prio=

6

tid=

0x01e82400

nid=

0x1740

waiting on condition

2

[

0x0414f000

]

3

java.lang.Thread.State: TIMED_WAITING (sleeping)

4

at java.lang.Thread.sleep(Native Method)

5

at inside.jvm.daemon.DaemonThread.run(DaemonTest.java:

34

)

6

at java.lang.Thread.run(Unknown Source)

7

8

Locked ownable synchronizers:

9

- None

10

11

"Low Memory Detector"

daemon prio=

6

tid=

0x01e50400

nid=

0xc78

runnable [

0x0000000

12

]

13

java.lang.Thread.State: RUNNABLE

14

15

Locked ownable synchronizers:

16

- None

17

18

"C1 CompilerThread0"

daemon prio=

10

tid=

0x01e4dc00

nid=

0x410

waiting on conditio

19

n [

0x00000000

]

20

java.lang.Thread.State: RUNNABLE

21

22

Locked ownable synchronizers:

23

- None

24

25

"Attach Listener"

daemon prio=

10

tid=

0x01e4a800

nid=

0x109c

waiting on condition

26

[

0x00000000

]

27

java.lang.Thread.State: RUNNABLE

28

29

Locked ownable synchronizers:

30

- None

31

32

"Signal Dispatcher"

daemon prio=

10

tid=

0x01e47800

nid=

0x9b0

runnable [

0x00000000

33

]

34

java.lang.Thread.State: RUNNABLE

35

36

Locked ownable synchronizers:

37

- None

38

39

"Finalizer"

daemon prio=

8

tid=

0x01e40400

nid=

0x17d0

in Object.wait() [

0x03f6f000

40

]

41

java.lang.Thread.State: WAITING (on object monitor)

42

at java.lang.Object.wait(Native Method)

43

- waiting on <

0x23d51148

> (a java.lang.ref.ReferenceQueue$Lock)

44

at java.lang.ref.ReferenceQueue.remove(Unknown Source)

45

- locked <

0x23d51148

> (a java.lang.ref.ReferenceQueue$Lock)

46

at java.lang.ref.ReferenceQueue.remove(Unknown Source)

47

at java.lang.ref.Finalizer$FinalizerThread.run(Unknown Source)

48

49

Locked ownable synchronizers:

50

- None

51

52

"Reference Handler"

daemon prio=

10

tid=

0x01e3b800

nid=

0x1064

in Object.wait() [

53

x03f1f000]

54

java.lang.Thread.State: WAITING (on object monitor)

55

at java.lang.Object.wait(Native Method)

56

- waiting on <

0x23d51048

> (a java.lang.ref.Reference$Lock)

57

at java.lang.Object.wait(Object.java:

485

)

58

at java.lang.ref.Reference$ReferenceHandler.run(Unknown Source)

59

- locked <

0x23d51048

> (a java.lang.ref.Reference$Lock)

60

61

Locked ownable synchronizers:

62

- None

63

64

"main"

prio=

6

tid=

0x014f9400

nid=

0x1154

waiting on condition [

0x001ff000

]

65

java.lang.Thread.State: TIMED_WAITING (sleeping)

66

at java.lang.Thread.sleep(Native Method)

67

at inside.jvm.daemon.DaemonTest.main(DaemonTest.java:

21

)

68

69

Locked ownable synchronizers:

70

- None

71

72

"VM Thread"

prio=

10

tid=

0x01dffc00

nid=

0xab8

runnable

73

74

"VM Periodic Task Thread"

prio=

10

tid=

0x01e6f400

nid=

0xcd8

waiting on condition

3、通过jconsole

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

JVM线程知多少

各线程介绍

  1. main线程

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

Reference Handler线程

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

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线程来说是一种灾难;

Signal Dispatcher线程

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

My Daemon Thread线程

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

Attach Listener线程

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

Low Memory Detector线程

网上查资料,说这个线程是负责对可使用内存进行检测,如果发现可用内存低,分配新的内存空间;我有两个疑问:

1) 在哪里分配新的内存?

2) 这个线程什么时候被创建的?代码在哪?

C1 CompilerThread0线程

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

VM Thread、VM Periodic Task Thread线程

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

希望牛人能指点一二,谢谢!

继续阅读