守护线程和非守护线程

一、前言

借鉴文章

文章1
文章2

①Java提供的两种线程

Java提供了两种线程: 守护线程和用户线程(非守护线程)

守护线程(Daemon Thread): 在程序运行时 在后台提供一种通用服务的线程,这种线程并不属于程序中不可或缺的部分。任何一个守护线程都是整个JVM中所有非守护线程的"保姆"。

  • 守护线程是一种在后台提供服务的线程,它的存在并不会阻止Java虚拟机(JVM)的退出。

  • 当所有的非守护线程执行完毕或者终止时,JVM会检查是否还有任何活动的守护线程。如果只剩下守护线程,JVM会优雅地关闭守护线程并退出。

  • 可以通过调用Thread类的setDaemon(true)方法将线程设置为守护线程。

用户线程(非守护线程,Non-daemon Thread): 用户线程基本上和守护线程一样,唯一的不同之处在于如果用户线程全部退出运行,只剩下守护线程存在了,JVM就会退出。因为当所有非守护线程结束时,没有了被守护者,守护线程也就没有工作可做,当然也就没有继续执行的必要了,程序就会终止,同时会杀死所有的"守护线程",也就是说只要有任何非守护线程还在运行,程序就不会终止

  • 非守护线程是一种正常的线程,它的存在会阻止JVM的退出,直到所有的非守护线程执行完毕或者手动调用System.exit()方法退出程序。
  • 默认情况下,通过Thread类创建的线程都是非守护线程。

②JVM正常情况的退出

JDK官方文档:

The Java Virtual Machine exits when the only threads running are all daemon threads.

当 JVM 中不存在任何一个正在运行的非守护线程时,则 JVM 进程即会退出。

③守护线程应用场景(作用)

当JVM中没有一个正在运行的非守护线程,这个时候JVM就会退出,也就是说守护线程拥有自动结束自己声明周期的特性,而非守护线程不具备这个特点JVM中的垃圾回收线程就是典型的守护进程,当非守护线程都没有了,那要垃圾回收线程干什么,直接结束JVM即可。守护线程一般用于执行一些后台任务,在程序或JVM退出时候,守护线程能够自动关闭,具体场景如下:

  1. 后台任务处理:守护线程通常用于执行一些后台任务,这些任务对于整个程序的运行并不是必需的,而且在程序的主要逻辑结束时可以被安全地终止。例如,日志记录、性能统计、定时任务等可以作为守护线程来执行。
  2. 服务监控和管理:守护线程常常被用来监控和管理其他线程或资源,确保它们的正常运行。例如,在服务器应用中,可以使用守护线程来监控网络连接、数据库连接等资源,及时释放无用资源或重新尝试连接。
  3. 辅助性工作:一些辅助性的工作可以交给守护线程来完成,比如周期性的数据清理、定时任务的执行等。
  4. 资源回收:守护线程通常用于执行垃圾回收(Garbage Collection)工作,确保程序的内存得到及时的回收和释放,以避免内存泄漏问题。

④Hook线程(下面会用到)

Hook线程概念:

Hook线程即钩子(Hook)线程,在程序即将退出,即JVM程序即将退出的时候,Hook线程会被启动执行。

Hook线程代码示例:

java 复制代码
package com;

import java.util.concurrent.TimeUnit;

/**
 * @author banana
 * @create 2023-11-07 16:27
 */
public class HookTask {
    public static void main(String[] args) {
        //为应用程序注入Hook(钩子)线程
        Runtime.getRuntime().addShutdownHook(new Thread(() ->{
            System.out.println("the hook thread 1 is running.");
            try {
                //休眠2秒
                TimeUnit.SECONDS.sleep(2);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("the hook thread 1 will exit");
        }));
        
        System.out.println("the main thread will exit.");
    }
}

运行结果:

可以看出,当主线程结束的,也就是JVM进程即将退出的时候,注入的Hook线程启动并打印相关日志。

注意事项:

  1. Hook 线程只有在正确接收到退出信号时,才能被正确执行,如果你是通过 kill -9这种方式,强制杀死的进程,那么抱歉,进程是不会去执行 Hook 线程的,为什么呢?你想啊,它自己都被强制干掉了,哪里还管的上别人呢?
  2. 请不要在 Hook 线程中执行一些耗时的操作,这样会导致程序长时间不能退出。

二、守护线程实例

①非守护线程不退出情况

代码:

java 复制代码
public class SHOUHUXCTS {

    public static void main(String[] args) {
        Thread thread = new Thread(new Runnable() {
            @Override
            public void run() {
                while(true){
                    //睡眠一会
                    try {
                        TimeUnit.SECONDS.sleep(1);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out.println("当前线程:" + Thread.currentThread().getName());
                }
            }
        });

        //启动用户线程(这里是非守护线程)
        thread.start();
        //主线程退出
        System.out.println("The main thread ready to exit...");
    }
}

运行结果:

可以看到,主线程main thread已经结束了,但是我们自定义的线程(非守护线程)是会持续运行的,即JVM进程依赖没有退出,当前非守护线程仍旧在运行中。

②守护线程退出情况

代码:

java 复制代码
package com.yjy;

import java.util.concurrent.TimeUnit;

/**
 * @author banana
 * @create 2023-11-07 15:44
 */
public class SHOUHUXCTS {

    public static void main(String[] args) {
        //设置一个钩子线程,在JVM退出时输出日志
        Runtime.getRuntime()
                .addShutdownHook(new Thread(() -> System.out.println("The JVM exit success")));


        Thread thread = new Thread(new Runnable() {
            @Override
            public void run() {
                while(true){
                    //睡眠一会
                    try {
                        TimeUnit.SECONDS.sleep(1);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out.println("当前线程:" + Thread.currentThread().getName());
                }
            }
        });

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

        //启动用户线程(这里是非守护线程)
        thread.start();

        //主线程退出
        System.out.println("The main thread ready to exit...");
    }
}

运行结果:

可以看到当主线程退出时,JVM会随之退出运行,守护线程也同时会被回收。

相关推荐
SofterICer几秒前
PE-PINCodes 规则
java
归去来兮★5 分钟前
c++面向对象
java·开发语言·c++
Aimyon_366 分钟前
DockerFile
java·开发语言
wrx繁星点点16 分钟前
创建型模式-单例模式
java·开发语言·单例模式
guangzhi063333 分钟前
JVM程序计数器
jvm
guangzhi063337 分钟前
JAVA执行引擎详细介绍
java·jvm
解孔明1 小时前
IDEA2023.1添加java虚拟机启动参数,打开断言
java·开发语言
关关不烦恼1 小时前
【Java数据结构】二叉树
java·开发语言·数据结构
苹果酱05671 小时前
使用 React Testing Library 测试自定义 React Hooks
java·开发语言·spring boot·后端·中间件
好奇的菜鸟1 小时前
Java技术体系:深入理解JDK与JRE及其产品线
java·开发语言