JAVA多线程基础篇--守护线程(Daemon Thread)

article/2025/10/3 7:22:18

1.概述

JAVA中的线程主要分为两类:用户线程(User Thread)和守护线程(Daemon Thread)。JAVA语言中无论是线程还是线程池,默认都是用户线程,因此用户线程也被称为普通线程。守护线程也被称之为后台线程、服务线程或精灵线程,守护线程是为用户线程服务的,当线程中的用户线程都执行结束后,守护线程也会跟随结束。守护线程具有自动结束生命周期的特性,而非守护线程则不具备该特性。本文将编写一些案例,同时根据源码来对用户线程和守护线程进行分析,帮助大家更好地理解。

2.守护线程分析

2.1 案例分析

上面讲了这么多概念,不如直接上一段代码来看一下:

import lombok.extern.slf4j.Slf4j;
import java.util.concurrent.TimeUnit;@Slf4j
public class DaemonThreadDemo {public static void main(String[] args) {log.info("当前线程:{}", Thread.currentThread().getName());//创建一个用户线程,一直运行Thread thread = new Thread(() -> {while (true) {try {TimeUnit.SECONDS.sleep(1);log.info("当前线程:【{}】,正在运行", Thread.currentThread().getName());} catch (InterruptedException e) {log.error("当前线程:{},休眠异常:{}", Thread.currentThread().getName(), e);}}}, "子线程");//启动线程thread.start();//主线程休眠2stry {TimeUnit.SECONDS.sleep(2);log.info("主线程:{},休眠2s", Thread.currentThread().getName());} catch (InterruptedException e) {log.error("主线程:{}休眠异常:{}", Thread.currentThread().getName(), e);}//主线程结束,打印信息log.info("主线程:{},结束运行", Thread.currentThread().getName());}
}

上述代码的主要运行流程如下图所示:
在这里插入图片描述
主线程main线程启动后,首先会去创建一个用户线程(该用户线程内部是一个死循环,会一直运行),接下来会启动该用户线程,最后主线程休眠并退出。上述代码中,线程启动后,当主线程运行结束后,JVM也不会退出,因为名称为子线程的用户线程还在后台运行,而且如无异常中断会一直运行。运行结果截图如下所示:
在这里插入图片描述
如果修改上述代码为守护线程呢?运行结果又会怎样?对上述代码做如下修改:

//新增监听JVM是否退出的Hook线程,Hook线程能够在JVM程序退出的时候被启动执行Runtime.getRuntime().addShutdownHook(new Thread(() -> {log.info("JVM运行结束......");}));//将线程设置为守护线程thread.setDaemon(true);

在这里插入图片描述
修改代码后,运行结果如下图所示:
在这里插入图片描述
由上述运行结果可知,当主线运行结束后,JVM也结束了运行,被设置为守护线程的子线程也结束了运行。

2.2 代码分析

2.2.1 设置线程为守护线程

线程可以通过 setDaemon(true) 方法将线程类型更改为守护线程,主要代码如下所示:

Thread thread = new Thread(()-> {@Overridepublic void run() {System.out.println("我是子线程");}});
//将线程设置为守护线程thread.setDaemon(true);

2.2.2 判断是否为守护线程

线程可以通过isDaemon()方法来判断当前线程或指定线程是否为守护线程,如果是守护线程则结果为true,否则为false。具体代码如下:

import lombok.extern.slf4j.Slf4j;@Slf4j
public class IsDaemonThread {public static void main(String[] args) {Runtime.getRuntime().addShutdownHook(new Thread(() -> {log.info("JVM运行结束......");}));Thread thread = new Thread(() -> {log.info("线程:{},是否是守护线程:{}", Thread.currentThread().getName(), Thread.currentThread().isDaemon());}, "子线程");thread.setDaemon(true);thread.start();log.info("主线程:{},IsDaemon:{}", Thread.currentThread().getName(), Thread.currentThread().isDaemon());}
}

运行结果为:
在这里插入图片描述

2.2.3 判断守护线程的优先级

在这里插入图片描述
运行结果如下图所示:
在这里插入图片描述
由上述运行结果可知,守护线程和用户线程是同一个优先级。我看部分文章或博客写的是用户线程优先级大于守护线程,如果有详细证明过程,可以推荐给我。

2.2.4 设置线程池为守护线程

如果设置线程池为守护线程,则需要将线程池中每个线程都设置为守护线程,具体代码如下:

import java.util.Set;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.ThreadFactory;public class ThreadPoolDaemonDemo {public static void main(String[] args) {ExecutorService threadPool = Executors.newFixedThreadPool(10, new ThreadFactory() {@Overridepublic Thread newThread(Runnable r) {Thread t = new Thread(r);// 设置线程为守护线程t.setDaemon(true);return t;}});System.out.println("线程池创建成功!");Thread thread1 = new Thread(() -> {System.out.println("hello ThreadPoolDaemonDemo1");});threadPool.submit(thread1);Set<Thread> threadSet = Thread.getAllStackTraces().keySet();for (Thread thread : threadSet) {System.out.println("thread.name=【" + thread.getName() + "】;group=" + thread.getThreadGroup() + ";isDaemon=" + thread.isDaemon() + ";priority=" + thread.getPriority());}}
}

线程运行结果为:
在这里插入图片描述
由上述结果看出,当将线程池设置为守护线程时,其内部线程都是守护线程;为了验证上述猜想,我编写了一个循环,测试了提交不同数量的线程任务,来查看该线程是否属于守护线程,结果表明均为守护线程。修改代码如下:

 for (int i = 0; i < 10; i++) {threadPool.submit(thread1);}

结果为:
在这里插入图片描述

2.2.5 判断守护线程子线程是否守护

下面编写一段测试代码,来判断守护线程创建的线程是否为守护线程,具体如下:
在这里插入图片描述
上述代码的运行结果如下:

在这里插入图片描述
由上述运行结果可知:守护线程中创建的子线程,默认情况下也属于守护线程

2.3 注意事项

1. setDaemon(true) 必须写在start方法前面;

setDaemon(true) 如果设置在 start() 之后,不但程序的执行会报错,而且设置的守护线程也不会生效。具体效果如下所示:

 @Slf4j
public class DaemonThreadDemo {public static void main(String[] args) {Runtime.getRuntime().addShutdownHook(new Thread(() -> {log.info("JVM运行结束......");}));Thread thread = new Thread(() -> {while (true) {try {TimeUnit.SECONDS.sleep(1);log.info("当前线程:【{}】,正在运行", Thread.currentThread().getName());log.info("当前线程:【{}】,优先级为:{}", Thread.currentThread().getName(), Thread.currentThread().getPriority());} catch (InterruptedException e) {log.error("当前线程:{},休眠异常:{}", Thread.currentThread().getName(), e);}}}, "子线程");thread.start();//设置守护线程thread.setDaemon(true);try {TimeUnit.SECONDS.sleep(2);log.info("主线程:{},休眠2s", Thread.currentThread().getName());} catch (InterruptedException e) {log.error("主线程:{}休眠异常:{}", Thread.currentThread().getName(), e);}log.info("当前线程:【{}】,优先级为:{}", Thread.currentThread().getName(), Thread.currentThread().getPriority());log.info("主线程:{},结束运行", Thread.currentThread().getName());}
}

在这里插入图片描述

2.不能把正在运行中的线程设置为守护线程;

3.复杂计算、资源回收这种,不适合使用守护线程来执行。

3.小结

1.守护线程是用来为用户线程服务的,当一个程序中的所有用户线程都结束之后,无论守护线程是否在工作都会跟随用户线程一起结束;
2.守护线程的子线程也是守护线程;
3.守护线程的优先级和用户线程优先级一致;
4.守护线程setDaemon(true) 如果设置在 start() 之后,程序执行会报错,守护线程也不会生效。

4.应用场景

1.垃圾回收线程就是典型的守护线程,随主线程结束而结束;
2.应用指标统计,部分服务可以通过守护线程来采取应用指标,服务结束则停止采集。

5.参考文献

1.https://www.jianshu.com/p/a157d749b5e8
2.https://www.cnblogs.com/quanxiaoha/p/10731361.html
3.https://juejin.cn/post/7006879369368961061
4.《JAVA多线程编程核心技术》-高洪岩著


http://chatgpt.dhexx.cn/article/6V0v3XFf.shtml

相关文章

面试官: 谈谈什么是守护线程以及作用 ?

文章首发自微信公众号: 小哈学Java 个人网站: https://www.exception.site/java-concurrency/java-concurrency-daemon-thread 目录 一、什么是守护线程 二、守护线程的作用及应用场景 三、总结 一、什么是守护线程 守护线程相对于正常线程来说&#xff0c;是比较特殊的一…

入门oracleDBA面试题

1.默认情况下管理员创建了一个用户&#xff0c;就会在 /home 下创建一个用户的主目录 2.当用mount进行设备或文件挂载时&#xff0c;需要用的设备名称位于 /dev 位置。 3.列出目录所有目录里的所有文件ls -l 4.Su 可以讲普通用户转换为超级用户&#xff0c;但是需要密码 5.…

oracle面试题答案,Oracle面试题笔试题及参考答案

一套Oracle面试题笔试题及参考答案 Oracle, 笔试, 面试 完成下列操作&#xff0c;写出相应的SQL语句 1.创建表空间neuspace&#xff0c;数据文件命名为neudata.dbf&#xff0c;存放在d:\data 目录下&#xff0c;文件大小为200MB&#xff0c;设为自动增长&#xff0c;增量5MB&am…

一个oracle面试题

某天&#xff0c;群里突然冒出一个道友询问oracle的面试题&#xff0c;特此记录一下 一系列过程&#xff1a; 第一步建表&#xff1a; DROP TABLE serv; DROP TABLE terminal; CREATE TABLE serv(serv_id NUMBER(10),prod_id NUMBER(10),user_type VARCHAR2(30),terminal_na…

oracle试题和答案,Oracle面试题及答案

Oracle面试题及答案模块和题目 l基本SQL查询 l运算符与函数 l子查询 l连接查询 建表语句emp.sql PartI(第一天) 01.查询员工表所有数据,并说明使用*的缺点 答&#xff1a; select*fromemp; 使用*的缺点有 a)查询出了不必要的列 b)效率上不如直接指定列名 02.查询职位(JOB)为PRE…

Oracle面试题整理

目录 Oracle面试题整理 1.MySQL和Oracle的区别&#xff1a; 2.Oracle中function和procedure的区别&#xff1f; 3. 比较truncate和delete命令 &#xff1f; 4.oralce中 rowid, rownum的定义 5. 事务的特性&#xff08;ACID&#xff09;是指什么 6. 列举几种表连接方式…

Oracle面试题及答案整理,速速收藏

点击上方SQL数据库开发&#xff0c;关注获取SQL视频教程 SQL专栏 SQL数据库基础知识汇总 SQL数据库高级知识汇总 好久没有给大家发面试题了&#xff0c;最近收集了一套Oracle的面试题&#xff0c;特地整理出来分享给大家&#xff0c;希望对你有帮助。 1、表&#xff1a;table1(…

Oracle数据库面试题

1、什么是数据库&#xff1f; 使用数据结构存储数据的仓库。 2、什么是关系型数据库&#xff1f; 使用关系模型存储数据的数据库。 关系模型指表与表之间的关系&#xff0c;包含一对一、一对多、多读多。 3、什么是非关系型数据库&#xff1f; 使用非关系模型保存数据的数据…

Oracle 面试题汇总

1&#xff0c;delete 与Truncate区别&#xff1f; 1&#xff09;Truncate 是DDL 语句&#xff0c;DELETE 是DML语句。 2&#xff09; Truncate 的速度远快于DELETE&#xff1b; 原因是&#xff1a; 当执行DELETE操作时所有表数据先被COPY到回滚表空间&#xff0c;数据量不同…

取消WIN10上shift键切换中英文输入法的设置

关闭全角半角切片: 设置 -> 时间和语言 -> 语言 -> 选项 -> 微软拼音输入法选项 -> 常规 -> 使用半角输入模式(开) Win10自带的输入法本人感觉还不错&#xff0c;但是在常用shift键实现其他相应功能的时候&#xff0c;&#xff08;如我经常用shift切换大小写…

Visual Studio 中英文切换

Visual Studio 中英文切换 今天发现Visual Studio用中文太没意思&#xff0c;于是研究了下如何改为英文版&#xff0c;这里以Visual Studio2017为例&#xff0c;如下&#xff1a; Visual Studio默认是不支持英文的&#xff0c;需要先安装下语言包&#xff1a; 找到Visual Stud…

pycharm中英文切换

1、关闭当前project 2、按照下图&#xff0c;如果有勾&#xff0c;就把勾去除&#xff0c;重新打开project&#xff0c;就是英文版了&#xff0c;反之是中文界面

springboot-页面中英文切换(国际化原理)

在springboot中有MessageSourceAutoConfiguration这个自动配置类&#xff0c;通过搜索找到这个类 首先可以看到这个类在配置文件中通过spring.messages.xxx来对对应properties中的属性进行配置&#xff0c;我们点进去properties这个对象里面&#xff0c;也就是类MessageSource…

winform中英文切换(实测简单可用)

Language改为英语或者英语-美国都行 2.再改为中文 就会在form1.cs里得到两个文件。分别在中文里填写中文&#xff0c;英文里填写英文&#xff0c;记得给控件后面加.Text 3.拖两个radiobutton控件放上来&#xff0c;加上这两段代码就可以实现中英文切换啦 // Language_Index 1/…

Visual studio的中英文切换

我们用visual studio &#xff0c; 有不同语言的版本。其实不同语言可以互相切换。 中文版去英文版是这样的&#xff1a; 打开vs菜单中的工具->选项 区域设置点击语言选择 然后选择英文 点击切换 英文版去中文版是&#xff1a;Tools-> Option International setting…

C# 中英文切换

简介&#xff1a; 一个项目如果要面对外国的客户时&#xff0c;需要语言切换。本文使用XML&#xff0c;XML是可扩展标记语言&#xff08;Extensible Markup Language&#xff09;的缩写&#xff0c;仅用于存储数据。 代码下载链接。 中文 英文 1.使用 新建三个文件。文件的路径…

html中文输入转换成英文,中英文转换,键盘如何中英文切换!

中英文的切换 首先在电脑上安装一个搜狗输入法&#xff0c;可以在搜狗搜索中下载安装。02 输入法安装完。 进入属性设置中&#xff0c;点击【按键】设置选项&#xff0c;首先看到的就是中英文切换设置&#xff0c;这时看到。shift键。楼上&#xff0c;ctrl空格 是切换中英文输入…

Android中英文切换

前言 在Android开发中&#xff0c;针对不同的客户群体&#xff0c;可能需要使用多国语言来对项目进行支持&#xff0c;那么&#xff0c;对于Android studio&#xff0c;如何实现这个功能呢&#xff1f;比如我们要实现Android程序中英文切换&#xff0c;该怎么做呢&#xff1f;…

Centos7 在英文环境中实现中英文切换

学习记录&#xff0c;参考&#xff1a;CentOS7英文环境下使用中文输入法 - InsideOut - 博客园 1. 安装中文拼音 #yum install ibus 2. 复制&#xff08;原理不太清楚&#xff09; #cp /usr/share/locale/zh_CN/LC_MESSAGES/ibus* /usr/share/locale/en_US/LC_MESSAGES 3.…

运用i18n 实现国际化(中英文切换)

使用 vue-i18n 进行Vue国际化处理&#xff0c;使项目切换中英文 1.下载安装插件 命令进入项目目录&#xff0c;执行以下命令安装vue 国际化插件vue-i18n npm install vue-i18n --save 2. 项目增加国际化翻译文件 在项目的src下添加lang文件夹增加中文翻译文件&#xff08;zh.j…