本文已在黄金档上发表,原文链接:http://www.goldendoc.org/2011/11/jvm-thread/
两个问题
- 什么是守护线程?守护线程与非守护线程有什么区别?其应用场景有哪些?
- 一个简单的Java程序,启动后JVM创建了哪些线程,它们的作用是什么?
熟悉上面两个问题的同学可以绕过了,不太熟的同学可以继续往下看,哈哈!
守护线程
守护线程,又叫Daemon线程,它有以下几个特点:
- 守护线程通常由虚拟机自己使用,比如垃圾收集器的线程;
- Java程序可以把它任何创建的线程标记为守护线程;但必须在线程运行前设置;
- Java初始线程(即开始于main方法的线程)是非守护线程;
- 只要还有任何非守护线程在运行,那么这个Java程序也在运行,即这个JVM实例还存活着;当JVM中的所有非守护线程都终止时,JVM实例将自动退出;
看下面这个例子:
1 |
public class DaemonTest {
|
3 |
public static void main(String[] args) throws InterruptedException,
|
5 |
Thread daemon = new Thread( new DaemonThread());
|
6 |
daemon.setName( "My Daemon Thread" );
|
7 |
daemon.setDaemon( true );
|
10 |
for ( int i = 0 ; i < 10 ; i++) {
|
11 |
System.out.println( "Main thread: " + i);
|
17 |
class DaemonThread implements Runnable {
|
18 |
private int index = 0 ;
|
23 |
System.out.println( "========= Daemon thread: " + index++);
|
26 |
} catch (InterruptedException e) {
|
主线程在创建一个Daemon线程后,循环10次结束;此时JVM中没有任何非Daemon线程,因此JVM将自动退出,其结果是导致创建的Daemon线程没有执行完既定任务就被迫终止了;程序结果如下:
2 |
========= Daemon thread: 0
|
3 |
========= Daemon thread: 1
|
8 |
========= Daemon thread: 10
|
结论:不要将业务逻辑封装在Daemon线程中执行,否则结果会不稳定;
JVM中的线程
上面例子中,虚拟机在启动后,一共创建了多少个线程呢?不用猜,我们通过程序或工具来看;
1、通过程序
代码如下:
1 |
public class JVMThreadTest {
|
3 |
public static void main(String[] args) {
|
4 |
Thread[] ts = getAllThread();
|
6 |
System.out.println(t.getId() + ": " + t.getName() + ", Priority: "
|
11 |
public static Thread[] getAllThread() {
|
12 |
ThreadGroup root = Thread.currentThread().getThreadGroup();
|
13 |
ThreadGroup ttg = root;
|
15 |
while ((ttg = ttg.getParent()) != null ) {
|
19 |
Thread[] tlist = new Thread[( int ) (root.activeCount() * 1.2 )];
|
20 |
return java.util.Arrays.copyOf(tlist, root.enumerate(tlist, true ));
|
上面的代码先取得所有的线程,然后依次打印其线程Id、线程名和优先级,结果如下:
1 |
2 : Reference Handler, Priority: 10
|
2 |
3 : Finalizer, Priority: 8
|
3 |
4 : Signal Dispatcher, Priority: 9
|
4 |
5 : Attach Listener, Priority: 5
|
2、通过jstack
通过jstack -l pid,可以得到如下栈信息:
1 |
"My Daemon Thread" daemon prio= 6 tid= 0x01e82400 nid= 0x1740 waiting on condition
|
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)
|
8 |
Locked ownable synchronizers:
|
11 |
"Low Memory Detector" daemon prio= 6 tid= 0x01e50400 nid= 0xc78 runnable [ 0x0000000
|
13 |
java.lang.Thread.State: RUNNABLE
|
15 |
Locked ownable synchronizers:
|
18 |
"C1 CompilerThread0" daemon prio= 10 tid= 0x01e4dc00 nid= 0x410 waiting on conditio
|
20 |
java.lang.Thread.State: RUNNABLE
|
22 |
Locked ownable synchronizers:
|
25 |
"Attach Listener" daemon prio= 10 tid= 0x01e4a800 nid= 0x109c waiting on condition
|
27 |
java.lang.Thread.State: RUNNABLE
|
29 |
Locked ownable synchronizers:
|
32 |
"Signal Dispatcher" daemon prio= 10 tid= 0x01e47800 nid= 0x9b0 runnable [ 0x00000000
|
34 |
java.lang.Thread.State: RUNNABLE
|
36 |
Locked ownable synchronizers:
|
39 |
"Finalizer" daemon prio= 8 tid= 0x01e40400 nid= 0x17d0 in Object.wait() [ 0x03f6f000
|
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)
|
49 |
Locked ownable synchronizers:
|
52 |
"Reference Handler" daemon prio= 10 tid= 0x01e3b800 nid= 0x1064 in Object.wait() [ 0
|
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)
|
61 |
Locked ownable synchronizers:
|
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 )
|
69 |
Locked ownable synchronizers:
|
72 |
"VM Thread" prio= 10 tid= 0x01dffc00 nid= 0xab8 runnable
|
74 |
"VM Periodic Task Thread" prio= 10 tid= 0x01e6f400 nid= 0xcd8 waiting on condition
|
3、通过jconsole
通过JDK自带的JConsole也可以查看JVM中的线程,如下图:
各线程介绍
- 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线程
关于上面三个线程,我同样存在以上两个疑问;
希望牛人能指点一二,谢谢!
分享到:
相关推荐
### JVM必知必会知识点梳理 #### 1. JVM的定义与层次 Java虚拟机(JVM)具有多重含义: - **一套规范**:即Java虚拟机规范,定义了Java虚拟机应该具有的行为。 - **一种实现**:例如HotSpot、J9、JRockit,它们都是...
Java虚拟机(JVM)是Java程序运行的基础,它负责管理程序的内存、执行字节码、处理异常等。以下是对JVM核心知识点的详细梳理和面试题的总结: 1. **内存结构** - **堆(Heap)**:所有线程共享的内存区域,用于...
Java虚拟机(JVM)是Java程序运行的核心组件,它为Java代码提供了跨平台的运行环境。在Java面试中,JVM的相关知识是面试官常常考察的重要领域,因为理解和掌握JVM的工作原理对于优化程序性能至关重要。以下是对JVM...
熟悉JUC有助于解决多线程环境下的并发问题,提高程序的并行执行效率。 3. **JVM(Java Virtual Machine)**:JVM是Java程序的运行环境,理解和优化JVM能够提升程序的性能。关键概念包括内存模型、类加载机制、垃圾...
### Java工程师必知必会的知识点 #### 一、Java语言基础 - **Java语言发展简史** - Java由Sun Microsystems公司在1995年发布。 - 初始目标是为了开发嵌入式设备和消费类电子产品。 - 由于其跨平台特性(Write ...
6. 多线程:Java 环境本身就是多线程的,Java 语言内置多线程控制,可以大大简化多线程应用程序开发。 四、Java 程序设计实践 1. 简单的 Java 程序设计:编写一个简单的 Java 程序输出“这是我的第一个 Java 程序...
这份包含36篇文章的文档集合,旨在深入剖析Java中的不为人知的问题,提供程序员必须掌握的关键知识点,以及如何应对面试中可能遇到的问题。下面将详细讨论这些文档可能涵盖的内容。 1. **内存管理与垃圾回收**:...
内容包括:Spring、SprngBoot、SpringCloud、Redis、MySQL、MyBatis、JVM、多线程和高并发、设计模式等 Spring框架:包括Spring的核心概念,如依赖注入和面向切面编程 Spring Boot:包括Spring Boot的基础知识,如...
Java面试通常涵盖以下几个核心领域:Java基础、集合框架、多线程并发以及虚拟机(JVM)知识。以下将对这些关键知识点进行详细阐述。 首先,Java基础知识是面试中的基石,包括但不限于变量、数据类型、运算符、流程...
- **线程池**:预创建线程,管理线程生命周期,减少频繁创建和销毁线程的开销。 #### 设计模式 - 设计模式包括创建型、结构型和行为型模式,是软件设计中解决问题的常见方法。 - **六大基本原则**:单一职责、开闭...
堆是JVM所管理的最大的一块内存空间,它是所有线程共享的一块内存区域。方法区用于存储已被虚拟机加载的类信息、常量、静态变量等数据。直接内存是Java堆外的、直接向系统申请的内存空间。 在JVM执行子系统中,关注...
不过,我可以根据我所知,提供一些关于Java虚拟机规范的基础知识点,希望能够满足您的需求。 Java虚拟机(JVM)是执行Java字节码的虚拟计算机。Java程序被编译成一种中间表示形式的字节码,JVM将这种字节码转换成...
本期内容包括JUC多线程并发、JVM和GC等目前大厂笔试中会考、面试中会问、工作中会用的高频难点知识。上半场,从多线程并发入手,分层递进讲解,逐步让大家掌握volatile、原子类和原子引用、CAS、ABA、Java锁机制、...
"2022河南知到java程序设计全部答案免费.pdf" 本资源主要讲解了 Java 程序设计的基础知识,包括选择题、简答题和综合编程题。下面是对每个知识点的详细解释: 一、选择题 1. (A)javac 是在 Dos 命令提示符下编译 ...
6. 多线程与并发编程:掌握多线程编程的原理,包括线程的创建、同步、死锁处理以及并发工具类的使用。了解Java并发包中的工具,比如Executor框架、CountDownLatch、CyclicBarrier、Semaphore等。 7. 输入输出(I/O)...
"Java面试必知必会Gothic主题"可能包含了一系列与Java核心技术、面试技巧以及常见问题相关的资料。下面我们将深入探讨一些Java面试中常见的核心知识点。 1. **基础语法**:面试通常会从Java的基础开始,如数据类型...
JVM面试专题及答案.pdf view面试专题.docx 中高级专题(View+Handler+Binder).docx 初级面试专题(中小厂).docx 多线程专题.docx 多线程面试专题及答案.pdf 大厂高端技术面试专题(有独立项目).docx 常见面试必问...