Java并发编程实战 09 | 为什么需要

news2024/11/25 18:37:23

什么是守护线程?

守护线程(Daemon Thread)是Java中的一种特殊线程,那么相对于普通线程它有什么特别之处呢?

在了解守护线程之前,我们先来思考一个问题:JVM在什么情况下会正常退出?答案是:当Java虚拟机(JVM)中只剩下守护线程在运行时,JVM就会自动退出。

上面这句话出自JDK官方文档,原话翻译过来是这样:当JVM中没有正在运行的非守护线程(用户线程)时,JVM进程就会自动退出。这句话可能有些难以理解,但通过下面的代码示例,你就能更清楚地了解它的含义。

public class Demo1 {

    public static void main(String[] args) throws InterruptedException {
        // 创建一个用户线程
        Thread userThread = new Thread(() -> {

            while (true) {
                System.out.println("User thread is running...");
                try {
                    Thread.sleep(2000L);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        });

        userThread.start();

        Thread.sleep(1000);
      
        Runtime.getRuntime().addShutdownHook(new Thread(() -> System.out.println("ShutdownHook is running...")));

        System.out.println("The main thread is about to finish execution");
    }
}

//输出:
User thread is running...
The main thread is about to finish execution
User thread is running...
User thread is running...
User thread is running...
User thread is running...
User thread is running...
..............

我们创建了一个用户线程,它的逻辑是一个无限循环。运行这段代码后,你可以猜一猜:当主线程结束后,JVM进程会自动退出吗?

答案是不会退出。因为有一个非守护线程(用户线程)一直在后台运行,JVM无法自动退出,ShutdownHook线程的相关逻辑也不会执行。相反,用户线程会继续运行,每隔2秒输出一次 “User thread is running…”。

那么,如果在这种情况下运行的线程是守护线程,又会发生什么呢?

可以通过调用 Thread.setDaemon(true) 方法将一个线程设置为守护线程,代码如下:

public class Demo2 {
    public static void main(String[] args) throws InterruptedException {
        // 创建一个守护线程
        Thread thread = new Thread(() -> {

            while (true) {
                System.out.println("Daemon thread is running...");
                try {
                    Thread.sleep(2000L);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        });

        thread.setDaemon(true);
        thread.start();

        Thread.sleep(1000);

        Runtime.getRuntime().addShutdownHook(new Thread(() -> System.out.println("ShutdownHook is running...")));

        System.out.println("The main thread is about to finish execution");
    }
}

//输出:
Daemon thread is running...
The main thread is about to finish execution
ShutdownHook is running...

在这种情况下,当主线程退出时,JVM 也会退出,并且所有守护线程都会被终止。即使守护线程中有死循环,也不会阻止 JVM 的退出。

守护线程的作用和使用场景

角色

在Java中,引入守护线程的主要目的是提供一种用于执行后台任务的机制。守护线程在程序的生命周期中起辅助作用,为其他线程提供支持和服务。

一个典型的例子是GC(垃圾回收器)线程,它就是一个典型的守护线程。它的主要作用是实时监控 JVM 的内存使用情况,并回收那些不再使用的对象,以释放内存空间。

当所有用户线程都结束运行时,程序的生命周期也就结束了,程序不会再产生新的垃圾,此时不再需要执行垃圾回收操作。因此,GC 线程也就没有继续运行的必要。因此,当垃圾回收线程是JVM中唯一剩下的线程时,JVM会自动退出。

应用场景
  • 后台任务:守护线程通常用于执行与主线程无关的后台任务,这些任务可以在程序后台静默运行。例如,日志记录、定时任务、系统监控等操作,都可以使用守护线程来完成。

  • 垃圾回收:垃圾回收是Java虚拟机的重要功能,它负责回收不再使用的对象并释放内存。垃圾回收器是一个守护线程,在程序运行时自动执行,清理不再需要的对象。

  • 资源管理:守护线程可以用于资源管理,例如管理数据库连接池的线程池。它可以监控空闲的数据库连接,如果连接空闲时间过长,守护线程可以自动关闭连接以避免资源浪费。

  • 后台服务:在服务器端应用中,守护线程常用于提供持续的服务。例如,在Web服务器中,守护线程可以监听客户端请求,当所有客户端连接断开时,守护线程会自动关闭服务器。

守护线程的注意事项

需要注意的是,守护线程的终止是不可控的,也就是说,守护线程的生命周期由 JVM 决定,而不是程序本身。当程序中只剩下守护线程时,它们会随着主线程的结束而自动终止。因此守护线程在终止时可能并没有机会去“干净地”完成正在进行的任务。

因此,在使用守护线程时,应确保它们执行的任务是可中断的或可恢复的,避免影响程序的整体逻辑。

可中断的任务意味着线程可以在被中断时安全地停止执行。例如,守护线程在进行一个长期任务时,应定期检查线程的中断状态,并在需要时退出或释放资源,这样可以防止数据损坏或丢失,例如如果一个守护线程正在执行一个关键的任务(如写入文件、数据库操作等),而此时 JVM 终止了它,可能导致数据的不完整或丢失。

public class SafeDaemonThreadExample {
    public static void main(String[] args) {
        // 创建一个守护线程
        Thread daemonThread = new Thread(() -> {
            try {
                while (true) {
                    System.out.println("守护线程正在运行...");
                    // 模拟执行某个长期任务
                    Thread.sleep(1000); // 任务期间可能会被中断
                }
            } catch (InterruptedException e) {
                System.out.println("守护线程被中断,安全退出...");
            }
        });

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

        // 主线程等待一段时间后退出
        try {
            Thread.sleep(3000);
            // 主线程结束前主动中断守护线程
            daemonThread.interrupt();
            System.out.println("主线程结束");
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

//输出:
守护线程正在运行...
守护线程正在运行...
守护线程正在运行...
主线程结束
守护线程被中断,安全退出...

可恢复的任务意味着当守护线程执行某些任务时,这些任务应该能够在中断或意外停止后恢复。也就是说,即使守护线程突然停止,这个任务所带来的影响应该是可以被补救的。例如,如果守护线程正在写入一个文件或者数据库,它应该能够保证:

  1. 不会因为突然停止而留下不完整或损坏的数据。
  2. 程序可以在稍后恢复此任务并继续进行。
不可恢复的守护线程
public class NonRecoverableDaemonExample {
    public static void main(String[] args) {
        // 创建一个守护线程
        Thread daemonThread = new Thread(() -> {
            while (true) {
                try {
                    // 模拟数据库更新
                    System.out.println("守护线程正在执行数据库更新操作...");
                    // 这里发生了数据更新操作
                    // 没有事务或恢复机制
                    Thread.sleep(1000); // 模拟任务执行时间
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        });

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

        // 主线程等待一段时间后退出
        try {
            Thread.sleep(3000);
            System.out.println("主线程结束");
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

在这个例子中:

  • 守护线程负责执行数据库的更新操作。
  • 守护线程在执行过程中没有使用任何事务(transaction)或恢复(recovery)机制。
  • 如果程序在更新操作的中间突然停止(例如,JVM 退出了,因为主线程结束),数据库的状态可能会变得不一致。比如,部分数据更新了,而其他数据没有更新,导致数据库中的数据处于一个不完整或损坏的状态。
可恢复的守护线程

现在,我们来看看如何设计一个可恢复的任务。这里我们使用事务(Transaction)来确保数据的一致性和完整性:

public class RecoverableDaemonExample {
    public static void main(String[] args) {
        // 创建一个守护线程
        Thread daemonThread = new Thread(() -> {
            while (true) {
                try {
                    // 模拟数据库更新操作
                    System.out.println("守护线程正在执行数据库更新操作...");

                    // 开始一个数据库事务
                    startTransaction();

                    // 执行更新操作
                    performDatabaseUpdate();

                    // 提交事务
                    commitTransaction();

                    Thread.sleep(1000); // 模拟任务执行时间
                } catch (InterruptedException e) {
                    System.out.println("守护线程被中断,安全退出...");
                } catch (Exception ex) {
                    // 回滚事务,确保数据不损坏
                    rollbackTransaction();
                    System.out.println("发生异常,已回滚事务以确保数据一致性");
                }
            }
        });

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

        // 主线程等待一段时间后退出
        try {
            Thread.sleep(3000);
            System.out.println("主线程结束");
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    // 模拟数据库操作方法
    private static void startTransaction() {
        System.out.println("开始事务");
    }

    private static void performDatabaseUpdate() {
        System.out.println("执行数据库更新操作");
    }

    private static void commitTransaction() {
        System.out.println("提交事务");
    }

    private static void rollbackTransaction() {
        System.out.println("回滚事务");
    }
}

修改后的守护线程在进行数据库更新时,使用了事务机制,如果在执行过程中发生异常或中断,事务会回滚(rollbackTransaction),确保所有的操作要么全部成功,要么全部失败。这样即使守护线程在中间停止,也不会造成数据损坏或丢失,保证了数据的一致性和完整性。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2118024.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

腾讯公众号种类这么多,为什么小程序能脱颖而出

在微信公众平台中,公众号和小程序是两种不同的功能实体,它们各自承担着不同的角色和使命。然而,随着小程序的崛起,它在众多功能中逐渐脱颖而出,成为商家和开发者的新宠。具体分析如下: 技术优势与用户体验 …

深入探索协同过滤:从原理到推荐模块案例

文章目录 前言一、协同过滤1. 基于用户的协同过滤(UserCF)2. 基于物品的协同过滤(ItemCF)3. 相似度计算方法 二、相似度计算方法1. 欧氏距离2. 皮尔逊相关系数3. 杰卡德相似系数4. 余弦相似度 三、推荐模块案例1.基于文章的协同过…

顶点照明渲染路径

1. 顶点照明渲染路径处理光照的方式 基本思想就是所有的光都按照逐顶点的方式进行计算的,在内置渲染管线中,它只会最多记录8个光源的数据,只会将光相关的数据填充到那些逐顶点相关的内置光源变量 顶点照明渲染路径仅仅是前向渲染路径的一个…

Mybatis-PlusDruid数据源

一、Mybatis-Plus简介 (一)什么是Mybatis-Plus Mybatis-Plus是一个Mybatis(OPENS NEW WINDOW)的增强工具,在Mybatis的基础上只做增强不做改变,为简化开发。 (二)Mybatis-Plus的优…

C语言之头文件,预处理命令#include

0 为什么要添加头文件?为什么要使用头文件? 可以看下下面图片左边,是不使用头文件,假设我们为了实现某些功能,编写的函数,全部声明在主函数之前,写几个函数还行,如果是大型项目&…

Hadoop压缩技术与Hive文件格式详解

目录 文件格式和压缩 Hadoop压缩概述 压缩格式 Hive文件格式 Text File ORC 1)文件格式 2)结构 3)建表语句 Parquet 1)文件格式 2)结构 3)建表语句 压缩 Hive表数据进行压缩 1)TextFil…

对话 IDC:一文带你了解低代码的技术趋势和平台选型

近日,葡萄城的活字格企业级低代码开发平台入选《中国低代码开发平台技术评估,2024》技术代表厂商,并在可视化开发、集成能力、开放性和兼容性以及生态能力维度获得五星评价。这一荣誉见证了葡萄城在低代码领域的技术实力与创新成果。 为进一…

Python 基本库用法:数学建模

文章目录 前言数据预处理——sklearn.preprocessing数据标准化数据归一化另一种数据预处理数据二值化异常值处理 numpy 相关用法跳过 nan 值的方法——nansum和nanmean展开多维数组(变成类似list列表的形状)重复一个数组——np.tile 分组聚集——pandas.…

不是你不想做 AI 产品,只是 UI 太贵劝退了你

"我有一个绝妙的 AI 产品创意!" "有想法就动起来呀~" "呃...还是算了吧,UI 太贵了,请不起" 相信不少人都有过这样的内心独白。 可能脑海中已经构思了无数个精彩的页面,甚至连细节都已经了然于胸。 但,在你准备大展拳脚的时候,现实给了…

鸿蒙开发5.0【弱引用】 使用

开发者需要依靠弱引用解决垃圾回收相关的内存管理问题。 强引用:默认的引用类型,被强引用的对象不会被垃圾回收。弱引用:允许对象在没有其他强引用时被垃圾回收,不会阻止垃圾回收器回收该对象。 场景一:使用弱引用打…

揭秘!老人帕金森与嗜睡之谜:是病症常态还是另有隐情?

在关爱老年人健康的道路上,帕金森病这一神经退行性疾病总是牵动着无数家庭的心。随着病情的发展,不少患者家属会发现,家里的老人似乎比以前更容易感到疲倦,甚至频繁出现嗜睡的情况。这不禁让人疑惑:老人帕金森会一直想…

数学建模——Box-Cox变换

用途:当某个随机变量 X X X 不服从正态分布的时候,可以尝试通过这种变换将其变成正态分布。 两个常用的变换 对数变换:已知随机变量 X X X,如果有 ln ⁡ X ∼ N ( μ , σ 2 ) \ln X\sim N(\mu,\sigma^2) lnX∼N(μ,σ2)&…

快被右下角的windows Defender烦死了,怎么让它消失?

前言 前段时间有小伙伴问:Windows自带的杀毒软件Windows Defender怎么关掉? 原因是无论在Windows Defender软件界面里怎么关闭,它都会拦截到某些软件和文件。 而且对于强迫症患者来说,右下角始终有个图标,还带了个x的…

【开源风云】从若依系列脚手架汲取编程之道(四)

📕开源风云系列 🍊本系列将从开源名将若依出发,探究优质开源项目脚手架汲取编程之道。 🍉从不分离版本开写到前后端分离版,再到微服务版本,乃至其中好玩的一系列增强Plus操作。 🍈希望你具备如下…

有了室内外一体化人行导航,你还怕迷路吗?

在快节奏的现代生活中,无论是穿梭于繁华的都市丛林,还是漫步于错综复杂的购物中心,迷路似乎成了不少人的“小确丧”。然而,随着科技的飞速发展,一项革命性的创新——室内外一体化人行导航系统,正悄然改变着…

CAN总线(一)

CAN总线(Controller Area Network Bus),从名字上可以知道,CAN总线构建的是一种局域网网路,每个挂载在CAN总线上的设备都可以利用这个局域网去发送自己的消息,也可以接收局域网的各种消息,每个设…

解锁高效驱动密码:SiLM8260A系列SiLM8260ABCS-DG 集成米勒钳位的双通道隔离驱动芯片

附上SiLM8260A同系列型号参考: SiLM8260ADCS-DG 12.5V/11.5V SiLM8260ABCS-DG 8.5V/7.5V SiLM8260AACS-DG 5.5V/5V SiLM8260AGCS-DG 3.5V/3V SiLM8260ABCS-DG是一款集成了米勒钳位功能的双通道隔离驱动芯片,它精准地满足了上述严苛条件。具备…

Excel排序错误原因之一

# Excel日常表格中的文字排序乱了,在系统语言更改成英语之后,不再按照首字母的顺序排列,且强制设置序列顺序也无济于事。 Excel表格内的排序尝试 表格基础选项就有“排序”,在其自定义选项中的“选项”设置处设置“拼音排序”&am…

2024年实体行业都在用的AI自动直播,有哪些可以发展的机会?

抖捧AI自动直播系统,是一款结合Ai和短视频营销的自动化直播辅助工具,针对企业和实体店有多重的应用场景,通过预设的直播脚本和智能回复逻辑,实现不需要真人参与,就可以实现全天候24小时直播,当用户进入直播…

【Java基础】ThreadLocal<LoginUser>:存储登录用户信息

💝💝💝欢迎来到我的博客,很高兴能够在这里和您见面!希望您在这里可以感受到一份轻松愉快的氛围,不仅可以获得有趣的内容和知识,也可以畅所欲言、分享您的想法和见解。 推荐:kwan 的首页,持续学…