在java多线程开发中,有两类线程,分别是User Thread(用户线程)和Daemon Thread(守护线程) 。

用户线程很好理解,我们日常开发中编写的业务逻辑代码,运行起来都是一个个用户线程。而守护线程相对来说则要特别理解一下。

守护线程,类似于操作系统里面是守护进程。由于Java语言机制是构建在JVM的基础之上,这一机制意味着Java平台是把操作系统的进程给屏蔽了。所以需要在JVM里面构造出对自己有利的机制,于是守护线程应运而生。

所谓的守护线程,指的是程序运行时在后台提供的一种通用服务的线程。比如垃圾回收线程就是一个很称职的守护者,并且这种线程并不属于程序中不可或缺的部分。因此,当所有的非守护线程结束时,程序也就终止了,同时会杀死进程中的所有守护线程。反过来说,只要任何非守护线程还在运行,程序就不会终止。

事实上,User Thread(用户线程)和Daemon Thread(守护线程)从本质上来说并没有什么区别,唯一的不同之处就在于虚拟机的离开:如果用户线程已经全部退出运行了,只剩下守护线程存在了,虚拟机也就退出了。因为没有了被守护者,守护线程也就没有工作可做了,也就没有继续运行程序的必要了。

守护线程并非只有虚拟机内部可以提供,用户也可以手动将一个用户线程设定/转换为守护线程。

在Thread类中提供了一个setDaemon(true)方法来将一个普通的线程(用户线程)设置为守护线程。

Java程序入口就是由JVM启动main线程,main线程又可以启动其他线程。当所有非守护线程都执行完毕后,无论有没有守护线程,JVM都会退出,进程结束。

public class test {

	public static void main(String[] args) {
		Thread t = new Thread1();
		t.setDaemon(true);//设置为守护线程
		t.start();
		System.out.println("main: wait 3 sec...");
		try {
			Thread.sleep(3000);//main线程睡了3秒,让Thread1线程执行
		} catch (InterruptedException e) {
		}
		System.out.println("main: end.");//main方法执行完毕,当前只有守护线程,进程终止
	}

}

class Thread1 extends Thread {

	public void run() {
		for (;;) {
			System.out.println("Thread-1: running...");
			try {
				Thread.sleep(1000);//每秒输出一次。3秒后,main线程醒了,开始执行
			} catch (InterruptedException e) {
			}
		}
	}
}

运行结果:

java守护线程和普通线程的区别 守护线程和用户线程_java守护线程和普通线程的区别

 

如果不设置Thread1为守护线程,即注释掉 t.setDaemon(true);

发现main方法即使完毕,进程也不会终止:

java守护线程和普通线程的区别 守护线程和用户线程_用户线程_02

 使用守护线程的注意几点:

1 . 必须在线程运行前确认是否为守护线程

thread.setDaemon(true)必须在thread.start()之前设置。否则将引发IllegalThreadStateException异常。这意味着正在运行的常规线程不能设置为守护进程线程。这显然不同于操作系统中的守护进程,在创建守护进程之后,让进程摆脱对原始会话的控制+让进程摆脱对原始进程组的控制+让进程摆脱对原始控制终端的控制;因此,虚拟机上的语言机制与系统级语言本质上是不同的。

2 . 守护线程创建的新线程也是守护线程

在守护线程中生成的新线程也是守护线程。这与操作系统中的守护进程本质上是不同的:守护进程之外的子进程不再是守护进程,尽管它复制有关父进程的进程相关信息。

3 . 守护线程不要去操作固有资源

并非所有用户线程都可以分配给守护线程进行服务,例如读写操作或计算逻辑。因为这个应用程序可能在Daemon Thread有时间操作之前就退出了虚拟机。这意味着守护进程线程永远不应该访问固有的资源,例如文件和数据库,因为它可以在任何时候被中断,甚至在操作的中间。

参考链接:多线程开发必看:守护线程优雅地停止用户线程

守护线程