【Java 基础篇】Java后台线程和守护线程详解

2023-09-20 21:28:20

在这里插入图片描述

在Java多线程编程中,有两种特殊类型的线程:后台线程(Daemon Thread)和守护线程(Daemon Thread)。这两种线程在一些特定的场景下非常有用,但也需要谨慎使用。本文将详细介绍后台线程和守护线程的概念、特性、用法,以及注意事项。

什么是后台线程和守护线程?

后台线程(Daemon Thread)

后台线程是一种特殊类型的线程,它的生命周期取决于是否存在任何前台线程。当所有的前台线程都结束时,后台线程会自动退出。与前台线程不同,后台线程不会阻止JVM的退出。后台线程通常用于执行一些支持性工作,如垃圾回收、周期性任务等。

后台线程的创建方式是将线程对象的setDaemon(true)方法设置为true,表示将该线程设置为后台线程。

Thread backgroundThread = new Thread(() -> {
    // 后台线程的工作
});
backgroundThread.setDaemon(true);
backgroundThread.start();

守护线程(Daemon Thread)

守护线程是后台线程的一种特例。它具有后台线程的特性,但通常用于执行一些系统服务或周期性任务,而不是支持性工作。与后台线程一样,守护线程的生命周期也取决于前台线程的存在。

Java中的垃圾回收器就是一个典型的守护线程的例子。垃圾回收线程会在程序运行过程中自动回收不再使用的内存,无需程序员干预。

后台线程和守护线程的特性

了解了后台线程和守护线程的概念,接下来我们来看看它们的特性。

特性一:生命周期取决于前台线程

后台线程和守护线程的生命周期都取决于是否还有前台线程在运行。如果所有前台线程都结束了,那么后台线程和守护线程会自动退出。

特性二:不阻止JVM退出

后台线程和守护线程不会阻止JVM的退出。这意味着,如果所有前台线程都结束了,JVM会正常退出,而不管后台线程和守护线程是否还在运行。

特性三:适用于支持性任务

后台线程通常用于执行一些支持性任务,如日志记录、定时任务、连接池维护等。它们不会干扰程序的正常运行,但在必要时可以执行一些必要的工作。

特性四:不建议进行I/O操作

由于后台线程和守护线程的生命周期不受控制,因此不建议在这些线程中执行涉及I/O操作的任务。因为在I/O操作中,线程可能需要等待外部资源,而这可能导致线程在不合适的时候退出,从而引发不可预料的问题。

使用后台线程和守护线程的场景

下面我们来看看使用后台线程和守护线程的一些常见场景。

场景一:定时任务

后台线程和守护线程非常适合执行定时任务。你可以创建一个后台线程或守护线程来执行周期性的任务,例如定时清理临时文件、定时发送心跳包等。

Thread timerThread = new Thread(() -> {
    while (true) {
        // 执行定时任务
        try {
            Thread.sleep(1000); // 暂停1秒钟
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
});
timerThread.setDaemon(true); //

 设置为守护线程
timerThread.start();

场景二:垃圾回收

垃圾回收器是Java中的经典守护线程的例子。垃圾回收线程会自动回收不再使用的内存,无需程序员的干预。这是Java内存管理的重要组成部分。

public class GarbageCollectorExample {
    public static void main(String[] args) {
        // 创建一个后台线程来执行垃圾回收
        Thread garbageCollectorThread = new Thread(() -> {
            while (true) {
                System.gc(); // 手动触发垃圾回收
                try {
                    Thread.sleep(60000); // 每分钟执行一次垃圾回收
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        });
        garbageCollectorThread.setDaemon(true); // 设置为守护线程
        garbageCollectorThread.start();

        // 模拟应用程序的主要工作
        for (int i = 0; i < 10; i++) {
            System.out.println("Main Thread is running...");
            try {
                Thread.sleep(2000); // 主线程每2秒输出一次
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

在上面的示例中,我们创建了一个后台线程 garbageCollectorThread,它会每分钟执行一次垃圾回收。主线程会模拟应用程序的主要工作。由于 garbageCollectorThread 是后台线程,当主线程结束时,它会自动退出。

场景三:日志记录

在某些情况下,你可能希望在后台记录日志,而不干扰主要的应用程序流程。后台线程可以用于将日志信息写入文件或发送到远程日志服务器。

public class LoggingExample {
    public static void main(String[] args) {
        // 创建一个后台线程来执行日志记录
        Thread loggingThread = new Thread(() -> {
            while (true) {
                logMessage("This is a log message.");
                try {
                    Thread.sleep(5000); // 每5秒记录一条日志
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        });
        loggingThread.setDaemon(true); // 设置为守护线程
        loggingThread.start();

        // 模拟应用程序的主要工作
        for (int i = 0; i < 10; i++) {
            System.out.println("Main Thread is running...");
            try {
                Thread.sleep(2000); // 主线程每2秒输出一次
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }

    private static void logMessage(String message) {
        // 此处可以将日志信息写入文件或发送到远程日志服务器
        System.out.println("Logging: " + message);
    }
}

在上面的示例中,我们创建了一个后台线程 loggingThread,它会每5秒记录一条日志。主线程模拟应用程序的主要工作。 logMessage 方法用于记录日志信息,你可以根据实际需求将日志信息写入文件或发送到远程日志服务器。由于 loggingThread 是后台线程,当主线程结束时,它会自动退出。

这些示例演示了如何使用后台线程执行垃圾回收和日志记录任务,同时确保这些线程不会阻止应用程序的正常退出。

使用注意事项

在使用后台线程和守护线程时,需要注意以下几点:

注意一:生命周期不可控

后台线程和守护线程的生命周期不受程序控制,所以在设计任务时要确保任务可以随时被中断或重启。

注意二:不要进行I/O操作

由于线程的随时退出特性,不建议在后台线程和守护线程中进行I/O操作,以避免不可预料的问题。

注意三:不要执行长时间任务

后台线程和守护线程通常用于执行一些短时间的任务,不适合执行长时间的计算或等待操作。如果需要执行长时间任务,应考虑使用普通线程。

总结

后台线程和守护线程是Java多线程编程中的两个特殊类型的线程,它们的生命周期取决于是否存在前台线程,不会阻止JVM的退出。这两种线程通常用于执行支持性任务、定时任务、垃圾回收等工作。然而,在使用它们时需要注意生命周期不可控、不要进行I/O操作以及不要执行长时间任务等问题。合理使用后台线程和守护线程可以提高程序的性能和可维护性,但需要根据具体需求谨慎选择。希望本文能够帮助读者更好地理解和使用后台线程和守护线程。

更多推荐

JVM 程序计数器

Java虚拟机(JVM,JavaVirtualMachine)是一个能够执行Java字节码的虚拟机。在JVM的架构中,程序计数器(ProgramCounter,PC)是一个关键的组成部分。程序计数器用于存储当前正在执行的Java字节码指令的地址。每个线程在JVM中都有自己的程序计数器。每条线程都需要有一个独立的程序计数

计算机视觉与深度学习-图像分割-视觉识别任务02-目标检测-【北邮鲁鹏】

目录标题参考目标检测定义深度学习对目标检测的作用单目标检测多任务框架多任务损失预训练模型姿态估计多目标检测问题滑动窗口(SlidingWindow)滑动窗口缺点AdaBoost(AdaptiveBoosting)参考区域建议selectivesearch思想慢速R-CNN慢速R-CNN思路边界框回归(Bboxreg)慢

leetcode分类刷题:队列(Queue)(二、优先队列解决TopK简单问题)

1、优先队列好像一般都叫堆,以大顶堆为例,顶部第一个元素最大,底部最后一个元素最小,自顶向底是递减的(更准确的说是非递增的),对外只能访问顶部第一个元素(对应索引为0)和底部最后一个元素(对应索引为-1);在Python中,heapq默认维护小顶堆,构造大顶堆时需要在入堆时添加相反数2、本次博客总结下用优先队列解决To

JavaScript系列从入门到精通系列第三篇:JavaScript基本语法(一)

文章目录一:JavaScript基本语法1:JS注释(一):JS多行注释(二):JS单行注释(三):JS中大小写(四):分号问题(五):空格和换行2:字面量和变量(一):字面量(二):变量(三):如何声明变量(四):如何给变量赋值(五):标识符二:JS中6种数据类型(一)1:JS字符串(一):JS字符串基本使用(二):

Ubuntu 安装 CUDA 与 OPENCL

前言:最近需要做一些GPU并行计算,因而入坑CUDA和OPENCL,两者都有用到一些,刚好有点时间,同时记录一些学习过程,排掉一些坑,这篇是环境安装篇,基本跟着走就没什么问题,环境:ubuntu18.04/ubuntu20.04显卡:Nvidia一、CUDA安装1.查看电脑是否识别GPUlspci|grep-invid

Liunx(Ubuntu20)常用指令

-rwxr-xr-x,在Linux系统中权限是区分用户的,即用户、组用户、其他用户,第一位表示文件的类型,-代表文件,d代表目录,其他每个用户占三个字符用户、组用户、其他用户都是rwx形式,其中r表示读、w表示写、x表示可执行,-表示没有权限,拿用户组举例,r只能出现在第一个位置、w只能出现在第二个位置、x只能出现在第

在 CentOS 上安装 Docker Engine

文章目录在CentOS上安装DockerEngine先决条件操作系统要求卸载旧版本安装方法使用rpm存储库安装设置存储库安装DockerEngine安装最新版本安装指定版本以非root用户身份管理Docker配置Docker以使用systemd启动参考官方文档:https://docs.docker.com/engin

阿里云服务器价格表,轻量和服务器最新活动价格表汇总

租用阿里云服务器怎么收费?阿里云服务器配置不同一年价格也不同,阿里云2核2G3M带宽108元一年、2核4G4M带宽297.98元12个月,云服务器u1公网带宽可选1M到5M,系统盘为ESSD云盘40GB起,CPU内存配置可选2核2G、2核4G、4核8G、8核16G等配置,还有ECS计算型c7、通用型g7和内存型r7多C

【SLAM】前端-视觉里程计之对极几何

文章目录【SLAM】前端-视觉里程计之对极几何1.对极几何2.本质矩阵及其求解3.单应矩阵及其求解3.三角测量4.思考4.1本质矩阵的自由度为多少4.2直接法求本质矩阵的过程涉及求解齐次线性方程,而对于齐次线性方程的解,要么只有零解,要么有无穷多个解,这里取哪一个解呢5.附录5.1相机成像模型【SLAM】前端-视觉里程

接口测试——接口协议抓包分析与mock_L1

目录:接口测试价值与体系常见的接口协议接口测试用例设计postman基础使用postman实战练习1.接口测试价值与体系接口测试概念接口:不同的系统之间相互连接的部分,是一个传递数据的通道接口测试:检查数据的交换、传递和控制管理过程接口测试的价值传统的测试方法成本急剧上升测试效率下降分层测试体系越往上,发现Bug的时间

CSS 布局 (三) 浮动、定位、多列布局

6、浮动最初用于在文本块内浮动图像,float属性成为在网页上创建多列布局最常用的工具之一。随着flexbox和grid的出现,它现在又回到了最初的目的,正如本文所解释的那样。6.1浮动的背景引入float属性是为了允许web开发人员实现包含图像在文本列内浮动的布局,文本在其左侧或右侧环绕。就像你在报纸版面上看到的那样

热文推荐