JAVA多线程基础篇--守护线程(Daemon Thread)
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多线程编程核心技术》-高洪岩著
相关文章:

JAVA多线程基础篇--守护线程(Daemon Thread)
1.概述 JAVA中的线程主要分为两类:用户线程(User Thread)和守护线程(Daemon Thread)。JAVA语言中无论是线程还是线程池,默认都是用户线程,因此用户线程也被称为普通线程。守护线程也被称之为后台线程、服务线程或精灵…...

对知识蒸馏的一些理解
知识蒸馏是一种模型压缩技术,它通过从一个大模型(教师模型)中传输知识到一个小模型(学生模型)中来提高学生模型的性能,知识蒸馏也要用到真实的数据集标签。 软损失soft loss就是拿教师模型在蒸馏温度为T的…...

概率论_概率公式中的分号(;)、逗号(,)、竖线(|) 及其优先级
目录 1.概率公式中的分号(;)、逗号(,)、竖线(|) 2.各种概率相关的基本概念 2.1 联合概率 2.2 条件概率(定义) 2.3 全概率(乘法公式的加强版) 2.4 贝叶斯公式 贝叶斯定理的公式推导 1.概率公式中的分号(;)、逗号(,)、竖线(|) ; 分号代表前后是两类…...

【C++】二叉树进阶 -- 详解
一、二叉搜索树概念 二叉搜索树 又称二叉排序树,它或者是一棵空树,或者是具有以下性质的二叉树: 若它的左子树不为空,则左子树上所有节点的值都小于根节点的值 若它的右子树不为空,则右子树上所有节点的值都大于根节点…...
K8S集群中Node节点资源不足导致Pod无法运行的故障排查思路
K8S集群中Node节点资源不足导致Pod无法运行的故障排查思路 Node节点资源不足可能会产生的故障 故障一:Pod数量太多超出物理节点的限制每一台Node节点中默认限制最多运行110个Pod资源,当一个应用程序有成百上千的Pod资源时,如果不扩容Node节…...
Node.js与npm版本比对
Node.js与npm版本比对 Node.js与npm版本比对版本对比表Node版本对比 Node.js与npm版本比对 我们在项目开发过程中,经常会遇到公司一些老的前端工程项目,而我们当前的node及npm版本都是相对比较新的了。 在运行以前工程时,会遇到相关环境不匹…...

智加科技与东风柳汽达成深度合作 自动驾驶重卡计划2024年初量产交付
(2023年10月19日,苏州)全球领先的重卡自动驾驶技术公司智加科技与东风柳汽宣布,双方共同开发的自动驾驶重卡H7计划2024年初实现量产交付。未来,双方将携手推出安全可靠、高性价比、性能卓越的自动驾驶重卡产品…...
mac下配置环境-node以及nvm
当前配置环境主要针对于mac下系统,需要提前安装brew包 如需要配置,可查阅:Brew包的基本安装(手把手教学)-CSDN博客 如果是window环境配置,分享一个不错的帖子:nvm的安装和使用(详细&…...
Elasticsearch基础篇(六):es创建映射和设置
es创建映射和设置 一、什么是 Elasticsearch 映射?二、映射中的字段类型常见字段类型 (Common data types)对象和关联类型(Objects and relational types)结构化数据类型(Structured data types)…...

机器人系统 ROS 常用命令行工具
1. 启动ros 主节点 roscore roscore运行成功如图: 1.1 rosrun 启动服务节点 例子:启动一个小乌龟节点 rosrun turtlesim turtlesim_node运行结果如图: 1.2 启动键盘控制 打开新的命令窗口,启动turtle_teleop_key 节点 rosr…...
Jasypt加解密、信息脱敏
文章目录 一、介绍二、Spring集成1、 Maven依赖2、application.xml的配置3、配置文件使用4、方法加密 二、SpringBoot集成1、 Maven依赖2、 Java Bean配置jasyptStringEncryptor3、配置文件使用4、Bean使用加密字段自动解密 一、介绍 Jasypt is a java library which allows th…...

力扣每日一题61:旋转链表
题目描述: 给你一个链表的头节点 head ,旋转链表,将链表每个节点向右移动 k 个位置。 示例 1: 输入:head [1,2,3,4,5], k 2 输出:[4,5,1,2,3]示例 2: 输入:head [0,1,2], k 4 输…...

SSM - Springboot - MyBatis-Plus 全栈体系(三十六)
第八章 项目实战 四、后台功能开发 3. 头条模块开发 3.1 登陆验证和保护 3.1.1 需求描述 客户端在进入发布页前、发布新闻前、进入修改页前、修改前、删除新闻前先向服务端发送请求携带 token 请求头后端接收 token 请求头后,校验用户登录是否过期并做响应前端根…...

作为开发的我能力模型图是什么样子的,应该如何去绘制?
作为开发的我能力模型图是什么样子的,应该如何去绘制? 能力模型图是一种用来描述个人或职位所需技能和能力的工具,对于开发人员来说,能力模型图通常包括技术能力、软技能和专业知识等多个维度。下面是一种可能的构建和绘制开发人员…...

【会议征稿通知】第三届密码学、网络安全和通信技术国际会议(CNSCT 2024)
第三届密码学、网络安全和通信技术国际会议(CNSCT 2024) 2024 3rd International Conference on Cryptography, Network Security and Communication Technology 随着互联网和网络应用的不断发展,网络安全在计算机科学中的地位越来越重要&…...

Python学习笔记——MYSQL,SQL核心
食用说明:本笔记适用于有一定编程基础的伙伴们。希望有助于各位! SQL语言分类 SQL注释 库管理 表管理 数据操作 分组聚合 分页限制 需要注意的是关键字的顺序不可以错乱,否则会报错其中LIMIT关键字的n是指从第n个开始,m是指查…...

集成学习方法之随机森林-入门
1、 什么是集成学习方法 集成学习通过建立几个模型组合的来解决单一预测问题。它的工作原理是生成多个分类器/模型,各自独立地学习和作出预测。这些预测最后结合成组合预测,因此优于任何一个单分类的做出预测。 2、 什么是随机森林 在机器学习中&…...

blender怎么在一个面上对半切割(不影响别的面)
1进入编辑模式 2.在面选择模式下,选中该物体需要切割成两半的面。 3.按K这个快捷键(切记,必须得用快捷键,不用的话没办法调出第一个绿色切割点),将切割点移动到需要切割的起始边,按住Shift键不放…...

vue3中使用vue3-pdf-app和使用浏览器内置的PDF插件浏览器PDF文件
文章目录 先准备一个PDF使用浏览器内置的PDF插件预览PDF在HTML中使用浏览器插件预览PDFVscode使用插件发布服务后直接通过URL地址访问PDF可使用的浏览器 在vue3项目中预览PDF文件vue3项目也是可以通过URL地址访问文件的vue3中使用浏览器内置的PDF插件预览PDF代码如下所示&#…...
fastadmin 后台添加视频
做个记录,字段自行对照解决 1.add.html <div class"form-group"><label class"control-label col-xs-12 col-sm-2">{:__(Video)}:</label><div class"col-xs-12 col-sm-8"><div class"input-group">&l…...
逻辑回归:给不确定性划界的分类大师
想象你是一名医生。面对患者的检查报告(肿瘤大小、血液指标),你需要做出一个**决定性判断**:恶性还是良性?这种“非黑即白”的抉择,正是**逻辑回归(Logistic Regression)** 的战场&a…...

Unsafe Fileupload篇补充-木马的详细教程与木马分享(中国蚁剑方式)
在之前的皮卡丘靶场第九期Unsafe Fileupload篇中我们学习了木马的原理并且学了一个简单的木马文件 本期内容是为了更好的为大家解释木马(服务器方面的)的原理,连接,以及各种木马及连接工具的分享 文件木马:https://w…...

基于TurtleBot3在Gazebo地图实现机器人远程控制
1. TurtleBot3环境配置 # 下载TurtleBot3核心包 mkdir -p ~/catkin_ws/src cd ~/catkin_ws/src git clone -b noetic-devel https://github.com/ROBOTIS-GIT/turtlebot3.git git clone -b noetic https://github.com/ROBOTIS-GIT/turtlebot3_msgs.git git clone -b noetic-dev…...

【笔记】WSL 中 Rust 安装与测试完整记录
#工作记录 WSL 中 Rust 安装与测试完整记录 1. 运行环境 系统:Ubuntu 24.04 LTS (WSL2)架构:x86_64 (GNU/Linux)Rust 版本:rustc 1.87.0 (2025-05-09)Cargo 版本:cargo 1.87.0 (2025-05-06) 2. 安装 Rust 2.1 使用 Rust 官方安…...

uniapp手机号一键登录保姆级教程(包含前端和后端)
目录 前置条件创建uniapp项目并关联uniClound云空间开启一键登录模块并开通一键登录服务编写云函数并上传部署获取手机号流程(第一种) 前端直接调用云函数获取手机号(第三种)后台调用云函数获取手机号 错误码常见问题 前置条件 手机安装有sim卡手机开启…...
C++课设:简易日历程序(支持传统节假日 + 二十四节气 + 个人纪念日管理)
名人说:路漫漫其修远兮,吾将上下而求索。—— 屈原《离骚》 创作者:Code_流苏(CSDN)(一个喜欢古诗词和编程的Coder😊) 专栏介绍:《编程项目实战》 目录 一、为什么要开发一个日历程序?1. 深入理解时间算法2. 练习面向对象设计3. 学习数据结构应用二、核心算法深度解析…...
云原生周刊:k0s 成为 CNCF 沙箱项目
开源项目推荐 HAMi HAMi(原名 k8s‑vGPU‑scheduler)是一款 CNCF Sandbox 级别的开源 K8s 中间件,通过虚拟化 GPU/NPU 等异构设备并支持内存、计算核心时间片隔离及共享调度,为容器提供统一接口,实现细粒度资源配额…...
区块链技术概述
区块链技术是一种去中心化、分布式账本技术,通过密码学、共识机制和智能合约等核心组件,实现数据不可篡改、透明可追溯的系统。 一、核心技术 1. 去中心化 特点:数据存储在网络中的多个节点(计算机),而非…...

解析“道作为序位生成器”的核心原理
解析“道作为序位生成器”的核心原理 以下完整展开道函数的零点调控机制,重点解析"道作为序位生成器"的核心原理与实现框架: 一、道函数的零点调控机制 1. 道作为序位生成器 道在认知坐标系$(x_{\text{物}}, y_{\text{意}}, z_{\text{文}}…...
无需布线的革命:电力载波技术赋能楼宇自控系统-亚川科技
无需布线的革命:电力载波技术赋能楼宇自控系统 在楼宇自动化领域,传统控制系统依赖复杂的专用通信线路,不仅施工成本高昂,后期维护和扩展也极为不便。电力载波技术(PLC)的突破性应用,彻底改变了…...