Java中的守护线程问题
作者:Jeffrey_oWang 发布时间:2023-09-13 11:49:23
守护线程
在Java中有两类线程
User Thread(用户线程)
Daemon Thread(守护线程)
守护线程的功能非常简单,在其本身是一个线程的同时,主要是为了给其他的线程提供服务,比如说计时器,清空高速缓存等等操作,守护线程具有和被守护线程一样的生命周期(这里并不是说守护线程和被守护线程常常是1-1的关系),当被守护线程死亡,守护线程往往也会死亡,当虚拟机中只剩下守护线程时,虚拟机就会退出,因为此时也没有运行程序的必要了
一个比较通俗的解释:任何一个守护线程都是整个JVM中所有非守护线程的保姆
只要当前JVM实例中尚存在任何一个非守护线程没有结束,守护线程就全部工作;只有当最后一个非守护线程结束时,守护线程随着JVM一同结束工作。
守护线程的作用是为其他线程的运行提供便利服务,守护线程最典型的应用就是 GC (垃圾回收器),它就是一个很称职的守护者。
需要注意的点是:
守护线程的优先级比较低
守护线程要注意考虑关机动作
守护线程应该永远不去访问固有资源,比如说文件或者数据库,因为它会在任何时候甚至一个操作的中间发生中断。
不要给守护线程分担读写逻辑或者计算逻辑,因为无法确定守护线程是否已经完成了工作,但是只要User退出守护线程也会立马结束,对于计算机程序来说这样的程序可能多次运行结果不一样,很显然这对于程序来说是毁灭性的。
操作:
通过thread.setDaemon(true) 将线程转换为守护线程
这个方法必须在thread.start()之前进行调用
守护线程与用户线程的区别
介绍Java守护线程与用户线程的概念和使用方法,以及相关注意事项。
1 定义和概述
Java 中的线程分为两类,分别为daemon 线程(守护线程〉和user 线程(用户线程)。守护线程又称Daemon线程,运行在后台,看不见;用户线程运行在前台,看的见。
在JVM启动时会调用main 函数, main 函数所在的线程就是一个用户线程,其实在JVM内部同时-还启动了好多守护线程, 比如垃圾回收线程。
Daemon线程是一种支持型线程,因为它主要被用作程序中后台调度以及支持性工作。这意味着,当一个Java虚拟机中不存在非Daemon线程的时候,Java虚拟机将会退出,而不管当前是否有守护线程,也就是说守护线程是否结束并不影响JVM的退出。
实际上,在main线程运行结束后,JVM会自动启动一个叫作DestroyJavaVM 的线程,该线程会等待所有用户线程结束后终止JVM 进程。
在Tomcat的NIO实现NioEndpoint中会开启一组接受线程来接受用户的连接请求,以及一组处理线程负责具体处理用户请求,在默认情况下,接受线程和处理线程都是守护线程,这意味着当tomcat 收到shutdown 命令后并且没有其他用户线程存在的情况下tomcat 进程会马上消亡,而不会等待处理线程处理完当前的请求。
2 使用守护线程
在线程start之前,可以通过调用thread.setDaemon(true)将线程设置为Daemon线程。
守护线程有两种结束方式:
守护线程也具有自己的run();方法,当后台线程完成自己的run方法后,守护线程结束。
用户线程运行结束,守护线程自动结束。
3 测试案例
public class Daemon {
//启动该类,将会构造两条线程,main线程和一条子线程。
public static void main(String[] args) throws InterruptedException {
//测试非守护线程
//可以看到,输出"main线程结束"之后,子线程还在继续输出,程序没有结束
// test1();
//测试守护线程
//可以看到,输出"main线程结束"之后,子线程没有继续输出,程序结束
test2();
}
/**
* 测试非守护线程
*
* @throws InterruptedException
*/
public static void test1() throws InterruptedException {
Thread thread = new Thread(() -> {
while (true) {
try {
Thread.currentThread().sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("子线程非守护线程");
}
});
thread.start();
Thread.currentThread().sleep(1000);
System.out.println("main线程结束");
}
//测试守护线程
public static void test2() throws InterruptedException {
Thread thread = new Thread(() -> {
while (true) {
try {
Thread.currentThread().sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("子线程守护线程");
}
});
thread.setDaemon(true);
thread.start();
Thread.currentThread().sleep(1000);
System.out.println("main线程结束");
}
}
使用jps查看java进程,可以发现如果子线程是守护线程那么主线程结束,子线程也结束了;如果子线程不是守护线程那么主线程结束,子线程没有结束。
4 注意事项
Daemon线程被用作完成支持性工作,但是在Java虚拟机退出时Daemon线程中的finally块并不一定会执行,如下代码:
public class Daemon {
public static void main(String[] args) {
Thread thread = new Thread(new DaemonRunner(), "DaemonRunner");
thread.setDaemon(true);
thread.start();
}
static class DaemonRunner implements Runnable {
@Override
public void run() {
try {
SleepUtils.second(10);
} finally {
System.out.println("DaemonThread finally run.");
}
}
}
}
运行Daemon程序,可以看到在控制台上没有任何输出。main线程(非Daemon线程)在启动了线程DaemonRunner之后随着main方法执行完毕而终止,而此时Java虚拟机中已经没有非Daemon线程,虚拟机需要退出。Java虚拟机中的所有Daemon线程都需要立即终止,因此DaemonRunner立即终止,但是DaemonRunner中的finally块并没有执行。
在构建Daemon线程时,不能依靠finally块中的内容来确保执行关闭或清理资源的逻辑。
来源:https://blog.csdn.net/weixin_46248230/article/details/121762171
猜你喜欢
- 目标:list中有0到39共40个元素,删除其中索引是10、20、30的元素方案一:使用普通for循环从前往后遍历再删除//初始化List列
- 1. 前言老板说,明天甲方要来看产品,你得造点数据,而且数据必须是“真”的,演示效果要好看一些,这样他才会买我们的产品,我好明年给你换个嫂子
- 队列的定义:队列(Queue)是只允许在一端进行插入,而在另一端进行删除的运算受限的线性表。 (1)允许删除的一端称为队头(Fro
- 选择排序:(Selection sort)是一种简单直观的排序算法,也是一种不稳定的排序方法。选择排序的原理一组无序待排数组,做升序排序,我
- 这篇文章主要介绍了如何通过Java实现时间轴过程解析,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友
- 本文实例讲述了WinFrom中label背景透明的实现方法,在应用程序开发中有着不错的实用价值。分享给大家供大家参考。具体实现方法如下:使用
- 封装(Encapsulation)是面向对象编程的一个核心概念,它意味着将数据(属性)和方法(操作数据的函数)捆绑在一起,形成一个类(Cla
- 1. Mybatis的@param注解自定义对象也用@param注解注:使用@param注解,mapper.xml 不加parameterT
- Pom依赖<parent> <groupId>org.springframework.bo
- 多线程编程多线程编程模式.NET 中,有三种异步编程模式,分别是基于任务的异步模式(TAP)、基于事件的异步模式(EAP)、异步编程模式(A
- 一、运算符用于创建对象和调用构造函数。这种大家都比较熟悉,没什么好说的了。二、修饰符在用作修饰符时,new 关键字可以显式隐藏从基类继承的成
- 一、运算符运算符包括下面几种:算术运算符赋值运算符比较运算符逻辑运算符位运算符三目运算符最不常用的是位运算符,但也是最接近计算机底层的。1、
- package com.letv.cloud.spider;import java.util.HashSet;import java.uti
- 前言自从用了SpringBoot,个人最喜欢的就是SpringBoot的配置文件了,和Spring比起SpringBoot更加灵活,修改的某
- 使用命令行的方式管理服务器镜像及容器是运维人员最常用的方式,但是有的时候我们不得不远程操作docker或者是面向对docker并不熟悉的技术
- 递归算法设计的基本思想是:对于一个复杂的问题,把原问题分解为若干个相对简单类同的子问题,继续下去直到子问题简单到能够直接求解,也就是说到了递
- C# 关于Invoke首先说下,invoke和begininvoke的使用有两种情况:control中的invoke、begininvoke
- 日期、数字格式化显示,是web开发中的常见需求,spring mvc采用XXXFormatter来处理,先看一个最基本的单元测试:packa
- 我们知道二维数组,是在一维数组的基础上进行了维度的增加。那么在实际使用的过程中,有时候我们所需要的二维数组,它们其中的维度是不同的,这就需要
- 概要本节要实现的是多表关联查询的简单demo。场景是根据id查询某商品分类信息,并展示该分类下的商品列表。一、Mysql测试数据新建表Cat