Java守护线程和用户线程的区别

 更新时间:2022年05月11日 17:23:56   作者:​ Java中文社群   ​  
这篇文章主要介绍了Java守护线程和用户线程的区别,用户线程和守护线程,默认情况下我们创建的线程或线程池都是用户线程,所以用户线程也被称之为普通线程,下文更多详细内容需要的小伙伴可以参考一下

前言:

在 Java 语言中,线程分为两类:用户线程和守护线程,默认情况下我们创建的线程或线程池都是用户线程,所以用户线程也被称之为普通线程。

想要查看线程到底是用户线程还是守护线程,可以通过 Thread.isDaemon() 方法来判断,如果返回的结果是 true 则为守护线程,反之则为用户线程。

我们来测试一下默认情况下线程和线程池属于哪种线程类型?测试代码如下:

import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

/**
 * 线程类型:守护线程 OR 用户线程
 */
public class ThreadType {
    public static void main(String[] args) {
        // 创建线程
        Thread thread = new Thread(new Runnable() {
            @Override
            public void run() {
                //...
            }
        });
        // 创建线程池
        ThreadPoolExecutor threadPool = new ThreadPoolExecutor(10, 10,
                0, TimeUnit.SECONDS, new LinkedBlockingQueue<>(100));
        threadPool.submit(new Runnable() {
            @Override
            public void run() {
                System.out.println("ThreadPool 线程类型:" +
                        (Thread.currentThread().isDaemon() == true ? "守护线程" : "用户线程"));
            }
        });
        System.out.println("Thread 线程类型:" +
                (thread.isDaemon() == true ? "守护线程" : "用户线程"));
        System.out.println("main 线程类型:" +
                (Thread.currentThread().isDaemon() == true ? "守护线程" : "用户线程"));
    }
}

以上程序的执行结果如下图所示: 

从上述结果可以看出,默认情况下创建的线程和线程池都是用户线程。

守护线程定义

守护线程(Daemon Thread)也被称之为后台线程或服务线程,守护线程是为用户线程服务的,当程序中的用户线程全部执行结束之后,守护线程也会跟随结束。 守护线程的角色就像“服务员”,而用户线程的角色就像“顾客”,当“顾客”全部走了之后(全部执行结束),那“服务员”(守护线程)也就没有了存在的意义,所以当一个程序中的全部用户线程都结束执行之后,那么无论守护线程是否还在工作都会随着用户线程一块结束,整个程序也会随之结束运行。

创建守护线程

我们可以通过 Thread.setDaemon(true) 方法将线程设置为守护线程,比如以下代码的实现:

public static void main(String[] args) {
    Thread thread = new Thread(new Runnable() {
        @Override
        public void run() {
            //...
        }
    });
    // 设置线程为守护线程
    thread.setDaemon(true);
    System.out.println("Thread 线程类型:" +
                       (thread.isDaemon() == true ? "守护线程" : "用户线程"));
    System.out.println("main 线程类型:" +
                       (Thread.currentThread().isDaemon() == true ? "守护线程" : "用户线程"));
}

以上程序的执行结果如下图所示: 

将线程池设置为守护线程

要把线程池设置为守护线程相对来说麻烦一些,需要将线程池中的所有线程都设置成守护线程,这个时候就需要使用线程工厂 ThreadFactory 来设置了(线程池中的所有线程都是通过线程工厂创建的),

它的具体实现代码如下:

public static void main(String[] args) throws InterruptedException {
    // 线程工厂(设置守护线程)
    ThreadFactory threadFactory = new ThreadFactory() {
        @Override
        public Thread newThread(Runnable r) {
            Thread thread = new Thread(r);
            // 设置为守护线程
            thread.setDaemon(true);
            return thread;
        }
    };
    // 创建线程池
    ThreadPoolExecutor threadPool = new ThreadPoolExecutor(10, 10,
                                                           0, TimeUnit.SECONDS, new LinkedBlockingQueue<>(100), threadFactory);
    threadPool.submit(new Runnable() {
        @Override
        public void run() {
            System.out.println("ThreadPool 线程类型:" +
                               (Thread.currentThread().isDaemon() == true ? "守护线程" : "用户线程"));
        }
    });
    Thread.sleep(2000);
}

以上程序的执行结果如下图所示: 

守护线程 VS 用户线程

通过前面的内容我们了解了什么是用户线程和守护线程了,那二者有什么区别呢?接下来我们用一个小示例来观察一下。 接下来我们将创建一个线程,分别将这个线程设置为用户线程和守护线程,在每个线程中执行一个 for 循环,总共执行 10 次信息打印,每次打印之后休眠 100 毫秒,来观察程序的运行结果。

用户线程

新建的线程默认就是用户线程,因此我们无需对线程进行任何特殊的处理,执行 for 循环即可(总共执行 10 次信息打印,每次打印之后休眠 100 毫秒),

实现代码如下:

public static void main(String[] args) throws InterruptedException {
    // 创建用户线程
    Thread thread = new Thread(new Runnable() {
        @Override
        public void run() {
            for (int i = 1; i <= 10; i++) {
                // 打印 i 信息
                System.out.println("i:" + i);
                try {
                    // 休眠 100 毫秒
                    Thread.sleep(100);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
    });
    // 启动线程
    thread.start();
}

以上程序的执行结果如下图所示: 

 从上述结果可以看出,当程序执行完 10 次打印之后才会正常结束进程。

守护线程

public static void main(String[] args) throws InterruptedException {
    // 创建守护线程
    Thread thread = new Thread(new Runnable() {
        @Override
        public void run() {
            for (int i = 1; i <= 10; i++) {
                // 打印 i 信息
                System.out.println("i:" + i);
                try {
                    // 休眠 100 毫秒
                    Thread.sleep(100);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
    });
    // 设置为守护线程
    thread.setDaemon(true);
    // 启动线程
    thread.start();
}

以上程序执行结果如下图所示: 

 从上述结果可以看出,当线程设置为守护线程之后,整个程序不会等守护线程 for 循环 10 次之后再进行关闭,而是当主线程结束之后,守护线程一次循环都没执行就结束了,由此可以看出守护线程和用户线程的不同。

守护线程注意事项

守护线程的设置 setDaemon(true) 必须要放在线程的 start() 之前,否则程序会报错。也就是说在运行线程之前,一定要先确定线程的类型,并且线程运行之后是不允许修改线程的类型的。 接下来我们来演示一下,如果在程序运行执行再设置线程的类型会出现什么问题?

演示代码如下:

public static void main(String[] args) throws InterruptedException {
    Thread thread = new Thread(new Runnable() {
        @Override
        public void run() {
            for (int i = 1; i <= 10; i++) {
                // 打印 i 信息
                System.out.println("i:" + i + ",isDaemon:" +
                            Thread.currentThread().isDaemon());
                try {
                    // 休眠 100 毫秒
                    Thread.sleep(100);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
    });
    // 启动线程
    thread.start();
    // 设置为守护线程
    thread.setDaemon(true);
}

以上程序执行结果如下图所示: 

 从上述结果可以看出,当我们将 setDaemon(true) 设置在 start() 之后,不但程序的执行会报错,而且设置的守护线程也不会生效。

总结

在 Java 语言中线程分为两类:用户线程和守护线程,默认情况下我们创建的线程或线程池都是用户线程,守护线程是为用户线程服务的,当一个程序中的所有用户线程都执行完成之后程序就会结束运行,程序结束运行时不会管守护线程是否正在运行,由此我们可以看出守护线程在 Java 体系中权重是比较低的,这就是守护线程和用户线程的区别。

到此这篇关于Java守护线程和用户线程的区别的文章就介绍到这了,更多相关Java 守护线程内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • SpringBoot自动配置实现的详细步骤

    SpringBoot自动配置实现的详细步骤

    这篇文章主要为大家介绍了SpringBoot自动配置实现详细的过程步骤,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2022-05-05
  • Java中的LinkedList底层源码分析

    Java中的LinkedList底层源码分析

    这篇文章主要介绍了Java中的LinkedList底层源码分析,底层基于双向链表,往LinkedList中间插入元素时,不需要移动大量的元素,只需要修改前后节点的指针,速度快,需要的朋友可以参考下
    2023-12-12
  • Java之遍历枚举值问题

    Java之遍历枚举值问题

    这篇文章主要介绍了Java之遍历枚举值问题,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2023-07-07
  • java实现文件的断点续传

    java实现文件的断点续传

    这篇文章主要为大家详细介绍了java实现文件的断点续传,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2022-06-06
  • 通过springboot+mybatis+druid配置动态数据源

    通过springboot+mybatis+druid配置动态数据源

    这篇文章主要介绍了通过springboot+mybatis+druid配置动态数据源,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,,需要的朋友可以参考下
    2019-06-06
  • SpringBoot中使用Servlet的两种方式小结

    SpringBoot中使用Servlet的两种方式小结

    这篇文章主要介绍了SpringBoot中使用Servlet的两种方式小结,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2022-07-07
  • Java Kubernetes本地部署的实现示例

    Java Kubernetes本地部署的实现示例

    本文,主要介绍了Java Kubernetes本地部署的实现,示例文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2025-10-10
  • SpringBoot2使用WebFlux函数式编程的方法

    SpringBoot2使用WebFlux函数式编程的方法

    这篇文章主要介绍了SpringBoot2使用WebFlux函数式编程的方法,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2018-08-08
  • SpringBoot yml配置敏感信息加密的实现

    SpringBoot yml配置敏感信息加密的实现

    本文主要介绍了SpringBoot yml配置敏感信息加密的实现,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2025-08-08
  • 浅析Spring Boot单体应用熔断技术的使用

    浅析Spring Boot单体应用熔断技术的使用

    这篇文章主要介绍了浅析Spring Boot单体应用熔断技术的使用,帮助大家更好的理解和使用spirngboot框架,感兴趣的朋友可以了解下
    2021-01-01

最新评论