首页 > 其他分享 >一篇搞定守护线程和非守护线程的区别

一篇搞定守护线程和非守护线程的区别

时间:2023-06-18 15:57:04浏览次数:44  
标签:搞定 Thread thread Daemon 线程 进程 守护

需求:如果想让某个线程随着主线程的结束而结束,该如何做?
  • 例如线程a如何随着主线程的结束而结束,解决这个问题我们就要应用线程的守护线程(后台线程),这样线程就会随着主线程的结束而结束。
在Java 中,可以创建两种线程
  • 守护线程
  守护线程 就是大家常说的 Daemon Thread 线程也叫 后台线程,是程序运行时在后台提供的一种通用服务的线程

 

  比如垃圾回收线程就是一个很称职的守护者,并且这种线程并不属于程序中不可或缺的部分。因此,当所有的非守护线程结束时,程序也就终止了,同时会杀死进程中的所有守护线程。反过来说,只要任何非守护线程还在运行,程序就不会终止。事实上,User Thread(用户线程)和 Daemon Thread(守护线程)从本质上来说并没有什么区别,唯一的不同之处就在于虚拟机的离开:如果用户线程已经全部退出运行了,只剩下守护线程存在了,虚拟机也就退出了。 因为没有了被守护者,守护线程也就没有工作可做了,也就没有继续运行程序的必要了。
  • 非守护线程

  非守护线程就是 大家常说的 User Thread(用户线程) 非后台线程。我们日常开发编写的业务逻辑代码,运行起来都是一个个用户线程。线程创建如果不指定Daemon参数为false, 默认为非守护线程,即是用户线程。

Java API 

 

守护线程的使用与注意事项。
  • 守护线程并非只有虚拟机内部可以提供,用户也可以手动将一个用户线程设定/转换为守护线程。
  • 在Thread类中提供了一个setDaemon(true)方法来将一个普通的线程(用户线程)设置为守护线程。
public final void setDaemon(boolean on);
在使用的过程中,有几点需要注意:
  1. thread.setDaemon(true)必须在thread.start()之前设置,否则会抛出一个IllegalThreadStateException异常。这也就意味着不能把正在运行的常规线程设置为守护线程。 这点与操作系统中的守护进程有着明显的区别,守护进程是创建后,让进程摆脱原会话的控制+让进程摆脱原进程组的控制+让进程摆脱原控制终端的控制;所以说寄托于虚拟机的语言机制跟系统级语言有着本质上面的区别。
  2. 在Daemon线程中产生的新线程也是Daemon的。关于这一点又是与操作系统中的守护进程有着本质的区别:守护进程fork()出来的子进程不再是守护进程,尽管它把父进程的进程相关信息复制过去了,但是子进程的进程的父进程不是init进程,所谓的守护进程本质上说就是,当父进程挂掉,init就会收养该进程,然后文件0、1和2都是/dev/null,当前目录到/
  3. 不是所有的应用都可以分配给Daemon线程来进行服务的,比如读写操作或者计算逻辑。因为这种应用可能在Daemon Thread还没来得及进行操作时,虚拟机已经退出了。这也就意味着,守护线程应该永远不去访问固有资源,如文件、数据库,因为它会在任何时候甚至在一个操作的中间发生中断。
下面以一个完成文件输出的守护线程任务作为例子:
 1 import java.io.*;  
 2 
 3 class TestRunnable implements Runnable {
 4     public void run(){
 5         try {
 6             Thread.sleep(1000); // 守护线程阻塞1秒后运行  
 7             File f = new File("daemon.txt");
 8             FileOutputStream os = new FileOutputStream(f,true);
 9             os.write("daemon".getBytes());
10         } catch(IOException e1) {  
11             e1.printStackTrace();  
12         } catch(InterruptedException e2) {  
13             e2.printStackTrace();  
14         }  
15     }  
16 }  
17 
18 public class TestDemo2 {
19     public static void main(String[] args) throws InterruptedException {
20         Runnable tr = new TestRunnable();
21         Thread thread = new Thread(tr);
22         thread.setDaemon(true); // 设置守护线程(必须在thread.start()之前)
23         thread.start(); // 开始执行分进程
24     }
25 }
View Code

上面这段代码的运行结果是文件daemon.txt中没有daemon字符串。

但是如果把thread.setDaemon(true);这行代码注释掉,文件daemon.txt是可以被写入daemon字符串的,因为这个时候这个线程就是普通的用户线程了。

简单理解就是,JRE判断程序是否执行结束的标准是所有的前台线程(用户线程)执行完毕了,而不管后台线程(守护线程)的状态。

守护线程的应用场景

  前面说了那么多,那么Daemon Thread的实际应用在那里呢?举个例子,Web服务器中的Servlet,在容器启动时,后台都会初始化一个服务线程,即调度线程,负责处理http请求,然后每个请求过来,调度线程就会从线程池中取出一个工作者线程来处理该请求,从而实现并发控制的目的。也就是说,一个实际应用在Java的线程池中的调度线程。

线程池 DefaultThreadFactory 类中创建守护线程代码

static class DefaultThreadFactory implements ThreadFactory {
    private static final AtomicInteger poolNumber = new AtomicInteger(1);
    private final ThreadGroup group;
    private final AtomicInteger threadNumber = new AtomicInteger(1);
    private final String namePrefix;
	
	DefaultThreadFactory() {
		SecurityManager s = System.getSecurityManager();
		group = (s != null)? s.getThreadGroup():Thread.currentThread().getThreadGroup();
		namePrefix = "pool-" + poolNumber.getAndIncrement() + "-thread-";
	}
	public Thread newThread(Runnable r) {
		Thread t = new Thread(group, r, namePrefix + threadNumber.getAndIncrement(),0);
		if (t.isDaemon()) t.setDaemon(false); // 设置守护线程
		if (t.getPriority() != Thread.NORM_PRIORITY)t.setPriority(Thread.NORM_PRIORITY);
		return t;
	}
}
  • 垃圾回收线程就是典型的守护线程,随主线程结束而结束;
  • 应用指标统计,部分服务可以通过守护线程来采取应用指标,服务结束则停止采集。
总结

我的理解,守护线程就是用来告诉JVM,我的这个线程是一个低级别的线程,不需要等待它运行完才退出,让JVM喜欢什么时候退出就退出,不用管这个线程。

在日常的业务相关的CRUD开发中,其实并不会关注到守护线程这个概念,也几乎不会用上。

但是如果要往更高的地方走的话,这些深层次的概念还是要了解一下的,比如一些框架的底层实现。

 

标签:搞定,Thread,thread,Daemon,线程,进程,守护
From: https://www.cnblogs.com/it-ws/p/17489126.html

相关文章

  • 多线程开启gprof性能测试的简易方法
    用到gprof时才知道,原来gprof只能对主线程统计耗时。manual上也没写线程相关的问题啊?不过有现成的解决方案:http://sam.zoy.org/writings/programming/gprof.html该方案封装了pthread_create(),让线程初始化执行一个setitimer(ITIMER_PROF,...)。简易的方法是直接在代码中写个setit......
  • 线程安全的随机数生成
    线程安全的随机数生成(金庆的专栏)*rand()不可重入*用线程专用存储保存随机数生成器*自动设置随机种子为设备随机数*线程退出时,自动析构生成器typedefboost::random::mt19937Generator;staticboost::thread_specific_ptr<Generator>s_pRng......
  • 后台用异步线程调用的场景与常用方式
    一.异步执行的场景:完成业务后,发短信、发邮件、微信公众号等消息推送提示的功能,可以采用异步执行。在导入数量量过大等情况下,可以使用异步导入的方式,提高导入时间等。...等等二.实现的方式:1.springboot中,进行线程池配置,然后用@Async标识异步执行方法即可,如下:(需要注意的@Enable......
  • Java中线程等待和唤醒
    Java中线程等待和唤醒本文主要是对Java中线程等待、唤醒相关的内容进行总结。线程的生命周期和状态Java线程在运行的生命周期中的指定时刻只可能处于下面6种不同状态的其中一个状态:NEW:初始状态,线程被创建出来但没有被调用start()。RUNNABLE:运行状态,线程被调用了s......
  • Spring框架中的线程池
    Spring框架中的线程池使用Java的ExecutorService接口实现ExecutorService是Java提供的用于管理线程池的高级工具。下面是在Spring框架中使用线程池的一般步骤:导入所需的依赖首先,确保你的项目中包含了使用线程池所需的依赖。通常情况下,你可以使用SpringBoot来创建项目,它会自动包含......
  • Java线程池与异常处理
    线程池线程池的创建代码ThreadPoolExecutorthreadPoolExecutor=newThreadPoolExecutor(intcorePoolSize,intmaximumPoolSize,longkeepAliveTime,TimeUnitunit,......
  • c++线程安全队列--有锁
    C++线程安全队列是一种数据结构,用于在多线程环境中安全地共享数据。它提供了一组功能,确保多个线程可以同时读取和写入队列,而不会导致竞争条件或数据损坏。C++线程安全队列的常见功能:入队操作(Enqueue):将一个元素添加到队列的尾部。这个操作必须是原子的,以确保在多线程环境中不会......
  • JAVA 线程安全案例
    #线程安全案例##使用原子类来实现资源的安全保护```javapublicclassAtomicSafeExample{staticCountDownLatchcountDownLatch=newCountDownLatch(2);publicstaticvoidmain(String[]args)throwsInterruptedException{Threadthread=newThrea......
  • 多线程
    多线程线程介绍每个进程都会有一个主线程,在创建进程时创建,往后创建的线程都属于子线程;线程在进程里不断抢占运行时间片;当进程遇到return结束,所有的线程全部结束。线程分类线程主要分为用户级线程和内核级线程用户级线程主要解决上下文切换问题,其调度由用户控制内核级线程......
  • 多线程
    1.进程和线程的定义进程:引用程序的执行实例(一个应用对应一个进程)线程:CPU调用和分派的基本单元,进程中执行运算的最小单位2.创建线程的种类继承java.lang.Thread类实现java.lang.Runnable接口3.继承java.lang.Thread类(1)定义MyThread类继承Thread类(2)重写run()方法,编写线程......