守护线程和非守护线程

一、前言

借鉴文章

文章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会随之退出运行,守护线程也同时会被回收。

相关推荐
上等猿4 分钟前
集合stream
java
java1234_小锋7 分钟前
MyBatis如何处理延迟加载?
java·开发语言
菠萝咕噜肉i8 分钟前
MyBatis是什么?为什么有全自动ORM框架还是MyBatis比较受欢迎?
java·mybatis·框架·半自动
林的快手23 分钟前
209.长度最小的子数组
java·数据结构·数据库·python·算法·leetcode
向阳12181 小时前
mybatis 缓存
java·缓存·mybatis
上等猿1 小时前
函数式编程&Lambda表达式
java
蓝染-惣右介1 小时前
【23种设计模式·全精解析 | 行为型模式篇】11种行为型模式的结构概述、案例实现、优缺点、扩展对比、使用场景、源码解析
java·设计模式
秋恬意2 小时前
IBatis和MyBatis在细节上的不同有哪些
java·mybatis
齐 飞2 小时前
BeanFactory和FactoryBean
java·sprint