java守护线程 作用_Java中守护线程的总结

article/2025/10/3 7:02:33

在Java中有两类线程:User Thread(用户线程)、Daemon Thread(守护线程)

用个比较通俗的比如,任何一个守护线程都是整个JVM中所有非守护线程的保姆:

只要当前JVM实例中尚存在任何一个非守护线程没有结束,守护线程就全部工作;只有当最后一个非守护线程结束时,守护线程随着JVM一同结束工作。

Daemon的作用是为其他线程的运行提供便利服务,守护线程最典型的应用就是 GC (垃圾回收器),它就是一个很称职的守护者。

User和Daemon两者几乎没有区别,唯一的不同之处就在于虚拟机的离开:如果 User Thread已经全部退出运行了,只剩下Daemon Thread存在了,虚拟机也就退出了。 因为没有了被守护者,Daemon也就没有工作可做了,也就没有继续运行程序的必要了。

值得一提的是,守护线程并非只有虚拟机内部提供,用户在编写程序时也可以自己设置守护线程。下面的方法就是用来设置守护线程的。

Thread daemonTread = new Thread();

// 设定 daemonThread 为 守护线程,default false(非守护线程)

daemonThread.setDaemon(true);

// 验证当前线程是否为守护线程,返回 true 则为守护线程

daemonThread.isDaemon();

这里有几点需要注意:

(1) thread.setDaemon(true)必须在thread.start()之前设置,否则会跑出一个IllegalThreadStateException异常。你不能把正在运行的常规线程设置为守护线程。

(2) 在Daemon线程中产生的新线程也是Daemon的。

(3) 不要认为所有的应用都可以分配给Daemon来进行服务,比如读写操作或者计算逻辑。

因为你不可能知道在所有的User完成之前,Daemon是否已经完成了预期的服务任务。一旦User退出了,可能大量数据还没有来得及读入或写出,计算任务也可能多次运行结果不一样。这对程序是毁灭性的。造成这个结果理由已经说过了:一旦所有User Thread离开了,虚拟机也就退出运行了。

//完成文件输出的守护线程任务

import java.io.*;

class TestRunnable implements Runnable{

public void run(){

try{

Thread.sleep(1000);//守护线程阻塞1秒后运行

File f=new File("daemon.txt");

FileOutputStream os=new FileOutputStream(f,true);

os.write("daemon".getBytes());

}

catch(IOException e1){

e1.printStackTrace();

}

catch(InterruptedException e2){

e2.printStackTrace();

}

}

}

public class TestDemo2{

public static void main(String[] args) throws InterruptedException

{

Runnable tr=new TestRunnable();

Thread thread=new Thread(tr);

thread.setDaemon(true); //设置守护线程

thread.start(); //开始执行分进程

}

}

//运行结果:文件daemon.txt中没有"daemon"字符串。

看到了吧,把输入输出逻辑包装进守护线程多么的可怕,字符串并没有写入指定文件。原因也很简单,直到主线程完成,守护线程仍处于1秒的阻塞状态。这个时候主线程很快就运行完了,虚拟机退出,Daemon停止服务,输出操作自然失败了。

public class Test {

public static void main(String args) {

Thread t1 = new MyCommon();

Thread t2 = new Thread(new MyDaemon());

t2.setDaemon(true); //设置为守护线程

t2.start();

t1.start();

}

}

class MyCommon extends Thread {

public void run() {

for (int i = 0; i 

System.out.println("线程1第" + i + "次执行!");

try {

Thread.sleep(7);

} catch (InterruptedException e) {

e.printStackTrace();

}

}

}

}

class MyDaemon implements Runnable {

public void run() {

for (long i = 0; i 

System.out.println("后台线程第" + i + "次执行!");

try {

Thread.sleep(7);

} catch (InterruptedException e) {

e.printStackTrace();

}

}

}

}

后台线程第0次执行!

线程1第0次执行!

线程1第1次执行!

后台线程第1次执行!

后台线程第2次执行!

线程1第2次执行!

线程1第3次执行!

后台线程第3次执行!

线程1第4次执行!

后台线程第4次执行!

后台线程第5次执行!

后台线程第6次执行!

后台线程第7次执行!

Process finished with exit code 0

从上面的执行结果可以看出:

前台线程是保证执行完毕的,后台线程还没有执行完毕就退出了。

实际上:JRE判断程序是否执行结束的标准是所有的前台执线程行完毕了,而不管后台线程的状态,因此,在使用后台县城时候一定要注意这个问题。

补充说明:

定义:守护线程--也称“服务线程”,在没有用户线程可服务时会自动离开。

优先级:守护线程的优先级比较低,用于为系统中的其它对象和线程提供服务。

设置:通过setDaemon(true)来设置线程为“守护线程”;将一个用户线程设置为

守护线程的方式是在 线程对象创建 之前 用线程对象的setDaemon方法。

example: 垃圾回收线程就是一个经典的守护线程,当我们的程序中不再有任何运行的

Thread,程序就不会再产生垃圾,垃圾回收器也就无事可做,所以当垃圾回收线程是

JVM上仅剩的线程时,垃圾回收线程会自动离开。它始终在低级别的状态中运行,用于

实时监控和管理系统中的可回收资源。

生命周期:守护进程(Daemon)是运行在后台的一种特殊进程。它独立于控制终端并且

周期性地执行某种任务或等待处理某些发生的事件。也就是

说守护线程不依赖于终端,但是依赖于系统,与系统“同生共死”。那Java的守护线程是

什么样子的呢。当JVM中所有的线程都是守护线程的时候,JVM就可以退出了;如果还有一个

或以上的非守护线程则JVM不会退出。

实际应用例子:

在使用长连接的comet服务端推送技术中,消息推送线程设置为守护线程,服务于ChatServlet的servlet用户线程,在servlet的init启动消息线程,servlet一旦初始化后,一直存在服务器,servlet摧毁后,消息线程自动退出

容器收到一个Servlet请求,调度线程从线程池中选出一个工作者线程,将请求传递给该工作者线程,然后由该线程来执行Servlet的 service方法。当这个线程正在执行的时候,容器收到另外一个请求,调度线程同样从线程池中选出另一个工作者线程来服务新的请求,容器并不关心这个请求是否访问的是同一个Servlet.当容器同时收到对同一个Servlet的多个请求的时候,那么这个Servlet的service()方法将在多线程中并发执行。

Servlet容器默认采用单实例多线程的方式来处理请求,这样减少产生Servlet实例的开销,提升了对请求的响应时间,对于Tomcat可以在server.xml中通过元素设置线程池中线程的数目。

如图:

27b7cd1c49a8cd1a14592c7f86a7eab1.png

为什么要用守护线程?

我们知道静态变量是ClassLoader级别的,如果Web应用程序停止,这些静态变量也会从JVM中清除。但是线程则是JVM级别的,如果你在Web 应用中启动一个线程,这个线程的生命周期并不会和Web应用程序保持同步。也就是说,即使你停止了Web应用,这个线程依旧是活跃的。正是因为这个很隐晦 的问题,所以很多有经验的开发者不太赞成在Web应用中私自启动线程。

如果我们手工使用JDK Timer(Quartz的Scheduler),在Web容器启动时启动Timer,当Web容器关闭时,除非你手工关闭这个Timer,否则Timer中的任务还会继续运行!

下面通过一个小例子来演示这个“诡异”的现象,我们通过ServletContextListener在Web容器启动时创建一个Timer并周期性地运行一个任务:

//代码清单StartCycleRunTask:容器监听器

package com.baobaotao.web;

import java.util.Date;

import java.util.Timer;

import java.util.TimerTask;

import javax.servlet.ServletContextEvent;

import javax.servlet.ServletContextListener;

public class StartCycleRunTask implements ServletContextListener ...{

private Timer timer;

public void contextDestroyed(ServletContextEvent arg0) ...{

// ②该方法在Web容器关闭时执行

System.out.println("Web应用程序启动关闭...");

}

public void contextInitialized(ServletContextEvent arg0) ...{

//②在Web容器启动时自动执行该方法

System.out.println("Web应用程序启动...");

timer = new Timer();//②-1:创建一个Timer,Timer内部自动创建一个背景线程

TimerTask task = new SimpleTimerTask();

timer.schedule(task, 1000L, 5000L); //②-2:注册一个5秒钟运行一次的任务

}

}

class SimpleTimerTask extends TimerTask ...{//③任务

private int count;

public void run() ...{

System.out.println((++count)+"execute task..."+(new Date()));

}

}

在web.xml中声明这个Web容器监听器:<?xml version="1.0" encoding="UTF-8"?>

com.baobaotao.web.StartCycleRunTask

在Tomcat中部署这个Web应用并启动后,你将看到任务每隔5秒钟执行一次。

运行一段时间后,登录Tomcat管理后台,将对应的Web应用(chapter13)关闭。

转到Tomcat控制台,你将看到虽然Web应用已经关闭,但Timer任务还在我行我素地执行如故——舞台已经拆除,戏子继续表演:

我们可以通过改变清单StartCycleRunTask的代码,在contextDestroyed(ServletContextEvent arg0)中添加timer.cancel()代码,在Web容器关闭后手工停止Timer来结束任务。

Spring为JDK Timer和Quartz Scheduler所提供的TimerFactoryBean和SchedulerFactoryBean能够和Spring容器的生命周期关联,在 Spring容器启动时启动调度器,而在Spring容器关闭时,停止调度器。所以在Spring中通过这两个FactoryBean配置调度器,再从 Spring IoC中获取调度器引用进行任务调度将不会出现这种Web容器关闭而任务依然运行的问题。而如果你在程序中直接使用Timer或Scheduler,如不 进行额外的处理,将会出现这一问题。


http://chatgpt.dhexx.cn/article/f6erOr4X.shtml

相关文章

【线程】什么是守护线程?

Java提供了两种线程&#xff1a;守护线程和用户线程 守护线程&#xff0c;是指在程序运行时 在后台提供一种通用服务的线程&#xff0c;这种线程并不属于程序中不可或缺的部分。通俗点讲&#xff0c;任何一个守护线程都是整个JVM中所有非守护线程的"保姆"。 用户线…

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

1.概述 JAVA中的线程主要分为两类&#xff1a;用户线程&#xff08;User Thread&#xff09;和守护线程(Daemon Thread)。JAVA语言中无论是线程还是线程池&#xff0c;默认都是用户线程&#xff0c;因此用户线程也被称为普通线程。守护线程也被称之为后台线程、服务线程或精灵…

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

文章首发自微信公众号: 小哈学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;…