在 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 ? "看护线程" : "用户线程"));
    }
}

以上程序的履行成果如下图所示:

面试突击31:什么是守护线程?它和用户线程有什么区别?
从上述成果能够看出,默许情况下创立的线程和线程池都是用户线程。

看护线程界说

看护线程(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 ? "看护线程" : "用户线程"));
}

以上程序的履行成果如下图所示:

面试突击31:什么是守护线程?它和用户线程有什么区别?

将线程池设置为看护线程

要把线程池设置为看护线程相对来说麻烦一些,需要将线程池中的一切线程都设置成看护线程,这个时分就需要使用线程工厂 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);
}

以上程序的履行成果如下图所示:

面试突击31:什么是守护线程?它和用户线程有什么区别?

看护线程 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();
}

以上程序的履行成果如下图所示:

面试突击31:什么是守护线程?它和用户线程有什么区别?
从上述成果能够看出,当程序履行完 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();
}

以上程序履行成果如下图所示:

面试突击31:什么是守护线程?它和用户线程有什么区别?
从上述成果能够看出,当线程设置为看护线程之后,整个程序不会等看护线程 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);
}

以上程序履行成果如下图所示:

面试突击31:什么是守护线程?它和用户线程有什么区别?
从上述成果能够看出,当咱们将 setDaemon(true) 设置在 start() 之后,不但程序的履行会报错,而且设置的看护线程也不会收效。

总结

在 Java 言语中线程分为两类:用户线程和看护线程,默许情况下咱们创立的线程或线程池都是用户线程,看护线程是为用户线程服务的,当一个程序中的一切用户线程都履行完结之后程序就会完毕运转,程序完毕运转时不会管看护线程是否正在运转,由此咱们能够看出看护线程在 Java 系统中权重是比较低的,这便是看护线程和用户线程的区别。

对错审之于己,毁誉听之于人,得失安之于数。

公众号:Java面试真题解析

面试合集:gitee.com/mydb/interv…