你了解Java中的守护线程吗?

文章目录
  1. 1. 概念
  2. 2. 特性
  3. 3. 使用
  4. 4. 示例
  5. 5. 异常
  6. 6. 守护线程 vs 用户线程

在Java中有两类线程:用户线程 (User Thread)、守护线程 (Daemon Thread)。

概念

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

特性

  • 所有用户线程完成执行时,守护线程无法阻止JVM的退出
  • 当所有用户线程完成执行时,JVM将退出
  • 如果JVM发现有正在运行的守护线程,它将结束这些线程然后退出。JVM不在乎守护线程是否正在运行。

将线程转换为守护线程可以通过调用Thread对象setDaemon(true)方法来实现。在使用守护线程时需要注意以下几点:

  1. thread.setDaemon(true)必须在thread.start()之前设置,否则会跑出一个IllegalThreadStateException异常。你不能把正在运行的常规线程设置为守护线程。

  2. Daemon线程中产生的新线程也是Daemon的。

  3. 守护线程应该永远不要去访问固有资源,如文件、数据库,因为它会在任何时候甚至在一个操作的中间发生中断。

    使用

  4. void setDaemon(boolean status):此方法用于将当前线程标记为守护线程用户线程。例如:如果我有一个用户线程tU,则tU.setDaemon(true)将该线程设为守护线程。相反,如果我有一个守护线程tD,则通过调用tD.setDaemon(false)将其设置为用户线程。

语法:

1
2
3
4
5
6
7
8
9
public final void setDaemon(boolean on)

参数:true
true : 将该线程设为守护线程
false : 将该线程设为用户线程(默认)

异常:
IllegalThreadStateException: 如果该线程正处于活动状态时,抛出异常
SecurityException: 当前线程无法修改该线程时,抛出异常
  1. boolean isDaemon():用于检查是否为守护线程。如果是守护线程,则返回true,否则返回false。

语法:

1
2
3
4
5
public final boolean isDaemon()

返回:
true : 守护线程
false : 用户线程

示例

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
public class DaemonThread extends Thread 
{
public DaemonThread(String name){
super(name);
}

public void run() {
// Checking whether the thread is Daemon or not
if(Thread.currentThread().isDaemon()) {
System.out.println(getName() + " is Daemon thread");
} else {
System.out.println(getName() + " is User thread");
}
}

public static void main(String[] args) {

DaemonThread t1 = new DaemonThread("t1");
DaemonThread t2 = new DaemonThread("t2");
DaemonThread t3 = new DaemonThread("t3");

// Setting user thread t1 to Daemon
t1.setDaemon(true); //必须在启动线程前调用

// starting first 2 threads
t1.start();
t2.start();

// Setting user thread t3 to Daemon
t3.setDaemon(true); //必须在启动线程前调用
t3.start();
}
}

输出:

1
2
3
t1 is Daemon thread
t3 is Daemon thread
t2 is User thread

这里的结果会有多种情况:

  1. 输出用户线程和守护线程的内容
  2. 仅输出用户线程的内容

通过结果就可以验证前面说的守护线程的特性

异常

如果在启动线程后调用setDaemon()方法,它将抛出IllegalThreadStateException

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public class DaemonThread extends Thread { 
public void run() {
System.out.println("Thread name: " + Thread.currentThread().getName());
System.out.println("Check if its DaemonThread: " + Thread.currentThread().isDaemon());
}

public static void main(String[] args) {
DaemonThread t1 = new DaemonThread();
DaemonThread t2 = new DaemonThread();
t1.start();

// Exception as the thread is already started
t1.setDaemon(true);

t2.start();
}
}

运行时异常:

1
2
3
Exception in thread "main" java.lang.IllegalThreadStateException
at java.lang.Thread.setDaemon(Thread.java:1352)
at DaemonThread.main(DaemonThread.java:19)

输出:

1
2
Thread name: Thread-0
Check if its DaemonThread: false

注意:不要在启动线程后调用setDaemon()方法

守护线程 vs 用户线程

  1. 当剩下的线程全是守护线程时,JVM就退出了。 因为没有了被守护者,守护线程也就没有工作可做了,也就没有继续运行程序的必要了。
  2. 守护线程为用户线程提供后台支持服务。