首页 > 编程语言 >【JAVA】深入理解守护线程与非守护线程:概念、应用及示例

【JAVA】深入理解守护线程与非守护线程:概念、应用及示例

时间:2024-08-16 17:25:50浏览次数:18  
标签:Thread 示例 System 线程 JVM println 守护

在这里插入图片描述

文章目录


更多相关内容可查看

介绍

在多线程编程中,线程的管理是至关重要的。Java 提供了两种主要的线程类型:守护线程和非守护线程。理解它们的区别以及如何在实际应用中使用它们,对编写高效和稳定的程序至关重要。本文将深入探讨这两种线程的概念、应用场景,并通过实际代码示例帮助你掌握如何使用它们。

1. 线程的基础知识

线程是进程中的一个执行单元,一个进程可以包含多个线程。线程的基本生命周期包括:

  • 新建(New):线程被创建,但尚未启动。
  • 就绪(Runnable):线程已准备好运行,并等待 CPU 调度。
  • 运行(Running):线程正在 CPU 上执行。
  • 阻塞(Blocked):线程因等待某些资源或条件而被阻塞。
  • 死亡(Terminated):线程执行完毕或被终止。

2. 守护线程与非守护线程

2.1 什么是守护线程?

守护线程是一种特殊类型的线程,它的存在不会阻止 JVM 退出。守护线程一般用于后台任务,如垃圾回收、监控任务等。当所有非守护线程都结束时,JVM 会退出,即使守护线程仍在运行。

特点:
  • 生命周期:守护线程的生命周期依赖于 JVM。当所有非守护线程完成后,JVM 会终止守护线程。
  • 用途:适用于后台处理,如定期清理操作、监控服务等。

2.2 什么是非守护线程?

非守护线程是普通线程,它会阻止 JVM 退出,直到它们完成执行。这类线程用于执行实际的应用任务,保证任务的完整性和稳定性。

特点:
  • 生命周期:非守护线程的生命周期独立于 JVM。JVM 会等到所有非守护线程完成后才退出。
  • 用途:适用于需要保证任务完成的场景,如数据库操作、用户请求处理等。

3. 为什么需要守护线程?

守护线程通常用于后台处理,避免了后台任务阻塞程序退出的情况。它们在完成任务时会自动结束,适合用于不影响程序主逻辑的服务。

示例:后台任务处理

假设我们有一个 Web 服务器,需要定期清理过期的会话数据。我们可以使用守护线程来实现这个功能,这样即使主线程结束,后台任务仍然可以在守护线程中运行。

public class DaemonThreadExample {
    public static void main(String[] args) {
        Thread cleanupThread = new Thread(() -> {
            while (true) {
                System.out.println("Cleaning up expired sessions...");
                try {
                    Thread.sleep(10000); // 每 10 秒执行一次清理
                } catch (InterruptedException e) {
                    System.err.println("Cleanup thread interrupted");
                }
            }
        });

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

        System.out.println("Main thread ending...");
        // 主线程结束后,JVM 会退出,守护线程也会被终止
    }
}

在上述代码中,cleanupThread 被设置为守护线程,它将在后台运行,定期执行清理任务。主线程结束时,JVM 会终止守护线程。

示例:日志记录

假设我们有一个日志记录的需求,我们希望将程序中发生的一些重要事件记录到文件中。为了避免日志记录的操作影响到主业务逻辑的执行,我们可以将日志记录的线程设为守护线程。

import java.io.FileWriter;
import java.io.IOException;
import java.io.PrintWriter;

public class DaemonThreadExample {
    
    public static void main(String[] args) {
        // 创建并启动日志记录的守护线程
        Thread logThread = new Thread(new LogTask());
        logThread.setDaemon(true); // 将线程设为守护线程
        logThread.start();
        
        // 主业务逻辑
        try {
            for (int i = 0; i < 5; i++) {
                System.out.println("Main thread is working: iteration " + i);
                Thread.sleep(1000); // 模拟主线程的工作
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        
        System.out.println("Main thread finished, program will exit soon.");
    }
    
    // 日志记录任务
    static class LogTask implements Runnable {
        @Override
        public void run() {
            try (PrintWriter writer = new PrintWriter(new FileWriter("log.txt", true))) {
                while (true) {
                    writer.println("Logging at " + System.currentTimeMillis());
                    writer.flush();
                    try {
                        Thread.sleep(2000); // 每2秒记录一次日志
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                        break;
                    }
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}

4. 非守护线程的应用场景

非守护线程用于执行核心业务逻辑,确保任务能够完整执行。比如数据库连接操作、文件处理等都需要在任务完成后才退出。

示例:数据库连接处理

假设我们有一个任务,需要打开一个数据库连接进行长时间的操作。我们可以使用非守护线程来确保连接在任务完成后正确关闭。

public class NonDaemonThreadExample {
    public static void main(String[] args) {
        Thread dbTaskThread = new Thread(() -> {
            System.out.println("Opening database connection...");
            try {
                // 模拟数据库操作
                Thread.sleep(15000); // 操作时间为 15 秒
                System.out.println("Database operation complete.");
            } catch (InterruptedException e) {
                System.err.println("Database task interrupted");
            } finally {
                System.out.println("Closing database connection...");
            }
        });

        dbTaskThread.start();

        try {
            dbTaskThread.join(); // 确保主线程等待 dbTaskThread 完成
        } catch (InterruptedException e) {
            System.err.println("Main thread interrupted");
        }

        System.out.println("Main thread ending...");
    }
}

在这个示例中,dbTaskThread 是一个非守护线程。主线程调用 join() 方法,确保在主线程退出之前,dbTaskThread 完成其数据库操作并关闭连接。

5. 守护线程与非守护线程的对比

特性守护线程非守护线程
生命周期JVM 退出时自动结束直到任务完成或被显式终止
适用场景背景任务、定时任务需要确保完成的核心业务任务
影响不会阻止 JVM 退出会阻止 JVM 退出,直到线程完成

6. 总结

守护线程和非守护线程各有其适用场景。守护线程适合后台服务任务,不会阻止 JVM 退出,而非守护线程适用于需要确保完成的任务。在编写多线程应用时,合理选择和使用这两种线程类型,可以提升程序的稳定性和性能。

标签:Thread,示例,System,线程,JVM,println,守护
From: https://blog.csdn.net/Aaaaaaatwl/article/details/141180956

相关文章

  • 进程与线程
    进程和线程的区别1.定义进程(Process):是操作系统中资源分配的基本单位。每个进程有自己的独立内存空间、文件描述符、程序计数器等资源。进程之间是相互独立的。线程(Thread):是操作系统调度的基本单位,一个进程可以包含多个线程,线程共享进程的内存空间和其他资源,但每个线程有自己......
  • DuckDB_SQL-使用示例以及和PG之间的概念
    duckdbCatalog(目录):表示整个数据库或数据库管理系统。一个数据库服务器可以包含多个数据库,每个数据库都有自己的Catalog1.database--catalogcatalog_namedatabase:In‑Memoryvs.PersistentDatabasedatabase--database_listnew_db.my_schema:system......
  • IMU惯性测量模块在ROS环境下的应用示例
    Ubuntu版本:20.04;ROS环境:noetic;IMU型号:亚博10轴IMU惯导模块目录一.ROS环境配置1、在终端运行对应的命令 2、安装ROS串口驱动二、IMU软件包使用1、新建、编译工作空间 2、绑定IMU端口3、修改参数配置 三、运行可视化界面 1、运行launch文件2、可能遇到的问题3、......
  • Linux打包命令tar极简示例_2
    只解压tar包中的某个文件这是tar包:只解压a.txt:上边的例子不大理想,再来一个tar包里带目录的:再弄个gzip压缩过的吧:......
  • 线程第二部分
    一、线程退出1.线程结束方式:    1.pthread_exit       2.在线程执行函数中return  (此时与1式相等)    3.pthread_cancel:    4.任何一个线程调用了exit或者主线程main函数return都会使进程结束2.pthread_cancel:intpthrea......
  • Java创建线程的方式
    1.继承Thread类第一步,创建一个线程类并继承Thread类第二步,重写run()方法,内部自定义线程执行体第三步,创建自定义的线程类对象,调用start()方法,启动线程示例代码如下publicclassMyThread1extendsThread{@Overridepublicvoidrun(){for(inti=0;i<......
  • 20240815有名管道双端线程通信
    //端1#include<stdio.h>#include<stdlib.h>#include<sys/types.h>#include<sys/stat.h>#include<fcntl.h>#include<unistd.h>#include<string.h>#include<pthread.h>#include<errno.h>#include<......
  • Linux线程
    一、线程的基本操作pthread_create(pthread_t*thread,constpthread_attr_t*attr,void*(*start_routine)(void*),void*arg) :此函数用于创建新线程。thread 用于存储新创建线程的标识符,attr 可指定线程属性,start_routine 是线程执行的函数指针,arg 为传递给线程执行......
  • miniomp.dll丢失全方位指南:解锁多线程应用程序的高效修复策略
    解决miniomp.dll丢失的问题,可以遵循以下专业步骤来确保多线程应用程序正常运行:1.手动下载miniomp.dll:•首先,从可信赖的DLL下载站点或软件供应商处找到适合您操作系统的miniomp.dll文件。请务必谨慎选择来源,以避免潜在的安全风险。2.放置DLL文件至正确路径:•将下载......
  • 线程池使用场景 调用多个微服务汇总数据
    importlombok.SneakyThrows;importjava.text.SimpleDateFormat;importjava.util.concurrent.*;publicclassT{@SneakyThrowspublicstaticvoidmain(String[]args){shopping_threadPool();}/*汇总数据使用线程池+Future耗时≈所有......