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 应用程序。