Java 中的守护线程(Daemon Threads)

更新于 2025-12-29

baeldung 2024-06-11

1. 概述

Java 中的守护线程(Daemon Threads)是用于在后台支持其他任务的线程,例如系统垃圾回收、日志记录、系统监控等。它们以较低的优先级运行,并且当所有用户线程(User Threads)执行完毕后,Java 虚拟机(JVM)会自动终止这些守护线程。许多 JVM 内部线程默认就是守护线程。

在本篇简短的文章中,我们将探讨守护线程的主要用途,并将其与用户线程进行比较。此外,我们还将演示如何通过编程方式创建、运行守护线程,并验证一个线程是否为守护线程。

2. 创建守护线程

要创建守护线程,只需调用 Thread API 中的 setDaemon() 方法即可。在下面的示例中,我们使用了一个继承自 Thread 类的 NewThread 类:

NewThread daemonThread = new NewThread();
daemonThread.setDaemon(true);
daemonThread.start();

任何线程都会继承创建它的线程的守护状态。由于主线程(main thread)是一个用户线程,因此在 main 方法中创建的所有线程默认都是用户线程。

需要注意的是,setDaemon() 方法只能在 Thread 对象创建之后、线程尚未启动之前调用。如果尝试在已启动的线程上调用该方法,将会抛出 IllegalThreadStateException 异常:

@Test(expected = IllegalThreadStateException.class)
public void whenSetDaemonWhileRunning_thenIllegalThreadStateException() {
    NewThread daemonThread = new NewThread();
    daemonThread.start();
    daemonThread.setDaemon(true);
}

此外,我们可以使用 isDaemon() 方法来检查一个线程是否为守护线程:

@Test
public void whenCallIsDaemon_thenCorrect() {
    NewThread daemonThread = new NewThread();
    daemonThread.setDaemon(true);
    daemonThread.start();
    assertTrue(daemonThread.isDaemon());

    NewThread userThread = new NewThread();
    userThread.start();
    assertFalse(userThread.isDaemon());
}

3. 守护线程与用户线程的区别

我们已经了解到,Java 提供了两种平台线程:用户线程和守护线程。用户线程是高优先级线程,用于执行应用程序的核心逻辑;而守护线程则是低优先级线程,其唯一作用是为用户线程提供服务。

以下是守护线程与用户线程之间的一些关键区别:

特性 守护线程(Daemon Threads) 用户线程(User Threads)
优先级 较低,主要用于后台服务 较高,用于主应用程序任务
JVM 行为 当仅剩守护线程运行时,JVM 会退出 只要有任意用户线程在运行,JVM 就会继续运行
典型用途 垃圾回收、系统监控等 应用程序逻辑、主程序流程
生命周期 所有用户线程结束后自动终止 需要手动终止

由于守护线程旨在为用户线程提供服务,只有在用户线程运行期间才有存在的必要,因此一旦所有用户线程执行完毕,JVM 不会等待守护线程完成,而是直接退出。

守护线程中可能存在无限循环,这通常不会造成问题。这是因为一旦所有用户线程结束,包括 finally 块在内的任何代码都不会被执行。因此,不建议在守护线程中执行 I/O 操作

然而,也存在例外情况。设计不良的守护线程代码可能会阻止 JVM 正常退出。例如,在一个正在运行的守护线程上调用 Thread.join() 可能会阻塞应用程序的关闭过程。

4. 结论

在本篇快速教程中,我们了解了什么是守护线程,以及它们在若干实际场景中的用途。随后,我们重点比较了守护线程与用户线程之间的差异。正确理解和使用守护线程,有助于构建更高效、更健壮的 Java 应用程序。