网站页面设计分析,界面设计心得体会,做网站然后推广,工厂生产管理系统软件作者 | 王磊来源 | Java中文社群#xff08;ID#xff1a;javacn666#xff09;转载请联系授权#xff08;微信ID#xff1a;GG_Stone#xff09;在 Java 语言中线程分为两类#xff1a;用户线程和守护线程#xff0c;而二者之间的区别却鲜有人知#xff0c;所以本文磊… 作者 | 王磊来源 | Java中文社群IDjavacn666转载请联系授权微信IDGG_Stone在 Java 语言中线程分为两类用户线程和守护线程而二者之间的区别却鲜有人知所以本文磊哥带你来看二者之间的区别以及守护线程需要注意的一些事项。1.默认用户线程Java 语言中无论是线程还是线程池默认都是用户线程因此用户线程也被称为普通线程。以线程为例想要查看线程是否为守护线程只需通过调用 isDaemon() 方法查询即可如果查询的值为 false 则表示不为守护线程自然也就属于用户线程了如下代码所示public static void main(String[] args) throws InterruptedException {Thread thread new Thread(new Runnable() {Overridepublic void run() {System.out.println(我是子线程);}});System.out.println(子线程守护线程 thread.isDaemon());System.out.println(主线程守护线程 Thread.currentThread().isDaemon());
}
以上程序的执行结果为从上述结果可以看出默认情况下主线程和创建的新线程都为用户线程。PSThread.currentThread() 的意思是获取执行当前代码的线程实例。2.主动修改为守护线程守护线程Daemon Thread也被称之为后台线程或服务线程守护线程是为用户线程服务的当程序中的用户线程全部执行结束之后守护线程也会跟随结束。守护线程的角色就像“服务员”而用户线程的角色就像“顾客”当“顾客”全部走了之后全部执行结束那“服务员”守护线程也就没有了存在的意义所以当一个程序中的全部用户线程都结束执行之后那么无论守护线程是否还在工作都会随着用户线程一块结束整个程序也会随之结束运行。那如何将默认的用户线程修改为守护线程呢这个问题要分为两种情况来回答首先如果是线程则可以通过设置 setDaemon(true) 方法将用户线程直接修改为守护线程而如果是线程池则需要通过 ThreadFactory 将线程池中的每个线程都为守护线程才行接下来我们分别来实现一下。2.1 设置线程为守护线程如果使用的是线程可以通过 setDaemon(true) 方法将线程类型更改为守护线程如下代码所示 public static void main(String[] args) throws InterruptedException {Thread thread new Thread(new Runnable() {Overridepublic void run() {System.out.println(我是子线程);}});// 设置子线程为守护线程thread.setDaemon(true);System.out.println(子线程守护线程 thread.isDaemon());System.out.println(主线程守护线程 Thread.currentThread().isDaemon());}
以上程序的执行结果为2.2 设置线程池为守护线程要把线程池设置为守护线程相对来说麻烦一些需要将线程池中的所有线程都设置成守护线程这个时候就需要使用 ThreadFactory 来定义线程池中每个线程的线程类型了具体实现代码如下// 创建固定个数的线程池
ExecutorService threadPool Executors.newFixedThreadPool(10, new ThreadFactory() {Overridepublic Thread newThread(Runnable r) {Thread t new Thread(r);// 设置线程为守护线程t.setDaemon(false);return t;}
});
如下图所示如上图所示可以看出整个程序中有 10 个守护线程都是我创建的。其他几种创建线程池的设置方式类似都是通过 ThreadFactory 统一设置的这里就不一一列举了。3.守护线程 VS 用户线程通过前面的学习我们可以创建两种不同的线程类型了那二者有什么差异呢接下来我们使用一个小示例来看一下。下面我们创建一个线程分别将这个线程设置为用户线程和守护线程在每个线程中执行一个 for 循环总共执行 10 次信息打印每次打印之后休眠 100 毫秒来观察程序的运行结果。3.1 用户线程新建的线程默认就是用户线程因此我们无需对线程进行任何特殊的处理执行 for 循环即可总共执行 10 次信息打印每次打印之后休眠 100 毫秒实现代码如下/*** AuthorJava中文社群*/
public class DaemonExample {public static void main(String[] args) throws InterruptedException {Thread thread new Thread(new Runnable() {Overridepublic 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 次打印之后才会正常结束进程。3.2 守护线程/*** AuthorJava中文社群*/
public class DaemonExample {public static void main(String[] args) throws InterruptedException {Thread thread new Thread(new Runnable() {Overridepublic 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 次之后再进行关闭而是当主线程结束之后守护线程只执行了一次循环就结束运行了由此可以看出守护线程和用户线程的不同。3.3 小结守护线程是为用户线程服务的当一个程序中的所有用户线程都执行完成之后程序就会结束运行程序结束运行时不会管守护线程是否正在运行由此我们可以看出守护线程在 Java 体系中权重是比较低的。4.守护线程注意事项守护线程的使用需要注意以下三个问题守护线程的设置 setDaemon(true) 必须要放在线程的 start() 之前否则程序会报错。在守护线程中创建的所有子线程都是守护线程。使用 jojn() 方法会等待一个线程执行完无论此线程是用户线程还是守护线程。接下来我们分别演示一下以上的注意事项。4.1 setDaemon 执行顺序当我们将 setDaemon(true) 设置在 start() 之后如下代码所示public static void main(String[] args) throws InterruptedException {Thread thread new Thread(new Runnable() {Overridepublic 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() 之后不但程序的执行会报错而且设置的守护线程也不会生效。4.2 守护线程的子线程public static void main(String[] args) throws InterruptedException {Thread thread new Thread(new Runnable() {Overridepublic void run() {Thread thread2 new Thread(new Runnable() {Overridepublic void run() {}});System.out.println(守护线程的子线程 thread2 isDaemon thread2.isDaemon());}});// 设置为守护线程thread.setDaemon(true);// 启动线程thread.start();Thread.sleep(1000);
}
以上程序执行结果如下从上述结果可以看出守护线程中创建的子线程默认情况下也属于守护线程。4.3 join 与守护线程通过 3.2 部分的内容我们可以看出默认情况下程序结束并不会等待守护线程执行完而当我们调用线程的等待方法 join() 时执行的结果就会和 3.2 的结果有所不同下面我们一起来看吧示例代码如下public static void main(String[] args) throws InterruptedException {Thread thread new Thread(new Runnable() {Overridepublic 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();// 等待线程执行完thread.join();System.out.println(子线程守护线程 thread.isDaemon());System.out.println(主线程守护线程 Thread.currentThread().isDaemon());
}
以上程序执行结果如下通过上述结果我们可以看出即使是守护线程当程序中调用 join() 方法时程序依然会等待守护线程执行完成之后再结束进程。5.守护线程应用场景守护线程的典型应用场景就是垃圾回收线程当然还有一些场景也非常适合使用守护线程比如服务器端的健康检测功能对于一个服务器来说健康检测功能属于非核心非主流的服务业务像这种为了主要业务服务的业务功能就非常合适使用守护线程当程序中的主要业务都执行完成之后服务业务也会跟随者一起销毁。6.守护线程的执行优先级首先来说线程的类型用户线程或守护线程并不影响线程执行的优先级如下代码所示定义一个用户线程和守护线程分别执行 10 万次循环通过观察最后的打印结果来确认线程类型对程序执行优先级的影响。public class DaemonExample {private static final int count 100000;public static void main(String[] args) throws InterruptedException {// 定义任务Runnable runnable new Runnable() {Overridepublic void run() {for (int i 0; i count; i) {System.out.println(执行线程 Thread.currentThread().getName());}}};// 创建守护线程 t1Thread t1 new Thread(runnable, t1);// 设置为守护线程t1.setDaemon(true);// 启动线程t1.start();// 创建用户线程 t2Thread t2 new Thread(runnable, t2);// 启动线程t2.start();}
}
以上程序执行结果如下通过上述结果可以看出线程的类型不管是守护线程还是用户线程对程序执行的优先级是没有任何影响的而当我们将 t2 的优先级调整为最大时整个程序的运行结果就完全不同了如下代码所示public class DaemonExample {private static final int count 100000;public static void main(String[] args) throws InterruptedException {// 定义任务Runnable runnable new Runnable() {Overridepublic void run() {for (int i 0; i count; i) {System.out.println(执行线程 Thread.currentThread().getName());}}};// 创建守护线程 t1Thread t1 new Thread(runnable, t1);// 设置为守护线程t1.setDaemon(true);// 启动线程t1.start();// 创建用户线程 t2Thread t2 new Thread(runnable, t2);// 设置 t2 的优先级为最高t2.setPriority(Thread.MAX_PRIORITY);// 启动线程t2.start();}
}
以上程序执行结果如下通过上述的结果可以看出程序的类型和程序执行的优先级是没有任何关系当新创建的线程默认的优先级都是 5 时无论是守护线程还是用户线程它们执行的优先级都是相同的当将二者的优先级设置不同时执行的结果也会随之改变优先级设置的越高最早被执行的概率也越大。7.总结在 Java 语言中线程分为用户线程和守护线程守护线程是用来为用户线程服务的当一个程序中的所有用户线程都结束之后无论守护线程是否在工作都会跟随用户线程一起结束。守护线程从业务逻辑层面来看权重比较低但对于线程调度器来说无论是守护线程还是用户线程在优先级相同的情况下被执行的概率都是相同的。守护线程的经典使用场景是垃圾回收线程守护线程中创建的线程默认情况下也都是守护线程。关注公号「Java中文社群」查看更多有意思、涨知识的并发编程文章。原创不易点个赞再走呗~