Java中的守护线程问题

 更新时间:2022年11月09日 16:17:57   作者:Jeffrey_oWang  
这篇文章主要介绍了Java中的守护线程问题,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教

守护线程

在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块中的内容来确保执行关闭或清理资源的逻辑。

以上为个人经验,希望能给大家一个参考,也希望大家多多支持脚本之家。

相关文章

  • Java裁剪压缩PNG图片,透明背景色变黑的解决方案

    Java裁剪压缩PNG图片,透明背景色变黑的解决方案

    这篇文章主要介绍了Java裁剪压缩PNG图片,透明背景色变黑的解决方案,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2021-11-11
  • Java EasyExcel实现合并相同内容单元格与动态标题功能

    Java EasyExcel实现合并相同内容单元格与动态标题功能

    这篇文章主要为大家详细介绍了Java EasyExcel如何实现合并相同内容单元格与动态标题功能,文中的示例代码讲解详细,有需要的小伙伴可以参考下
    2023-12-12
  • Java集合框架之Map详解

    Java集合框架之Map详解

    这篇文章主要为大家详细介绍了Java集合框架之Map,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下,希望能够给你带来帮助
    2022-03-03
  • Java操作mongodb增删改查的基本操作实战指南

    Java操作mongodb增删改查的基本操作实战指南

    MongoDB是一个基于分布式文件存储的数据库,由c++语言编写,旨在为WEB应用提供可扩展的高性能数据存储解决方案,下面这篇文章主要给大家介绍了关于Java操作mongodb增删改查的基本操作实战指南,需要的朋友可以参考下
    2023-05-05
  • MyBatis-Plus中静态工具Db的多种用法及实例分析

    MyBatis-Plus中静态工具Db的多种用法及实例分析

    本文将详细讲解MyBatis-Plus中静态工具Db的各种用法,并结合具体案例进行演示和说明,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教
    2025-03-03
  • SpringCloud集成MybatisPlus实现MySQL多数据源配置方法

    SpringCloud集成MybatisPlus实现MySQL多数据源配置方法

    本文详细介绍了SpringCloud集成MybatisPlus实现MySQL多数据源配置的方法,包括在application.properties中配置多数据源,配置MybatisPlus,创建Mapper接口和使用多数据源等步骤,此外,还解释了每一个配置项目的含义,以便读者更好地理解和应用
    2024-10-10
  • Java Swing实现记事本页面

    Java Swing实现记事本页面

    这篇文章主要为大家详细介绍了Java Swing实现记事本页面,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2022-02-02
  • Java AQS(AbstractQueuedSynchronizer)源码解析

    Java AQS(AbstractQueuedSynchronizer)源码解析

    AbstractQueuedSynchronizer被称为队列同步器,简称为大家熟知的AQS,这个类可以称作concurrent包的基础。本文将通过剖析源码来看看AQS是如何工作的,感兴趣的可以了解一下
    2023-02-02
  • Java毕业设计之多用户宿舍管理系统的实现

    Java毕业设计之多用户宿舍管理系统的实现

    这篇文章主要介绍了基于Java实现的多用户宿舍管理系统,本文采用了jsp、servlet、jdbc等技术,文中示例代码讲解详细,需要的可以参考一下
    2022-02-02
  • Java String方法获取字符出现次数及字符最大相同部分示例

    Java String方法获取字符出现次数及字符最大相同部分示例

    这篇文章主要介绍了Java String方法获取字符出现次数及字符最大相同部分,涉及java字符串的遍历、比较、计算等相关操作技巧,需要的朋友可以参考下
    2017-09-09

最新评论