Java守护线程

Java中的守护线程是用于服务其他线程的,当所有非守护线程结束时,JVM会退出。守护线程如GC线程,可通过Thread的setDaemon(true)方法设置。设置守护线程需在start()之前,且新线程会继承父线程的守护状态。示例展示了守护线程与非守护线程在程序退出上的区别。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

一、概念简介 

在java的线程中,除了我们熟知的线程外,有一种特殊的线程,一直在为其他线程提供服务,这就是守护线程。从它的意思上来看,就是为了守护其他的线程而存在的,使用上用到了我们之前所学的Thead类。下面我们就守护线程的概念、使用事项及继承父线程的实例为大家带来介绍。

Java中,通过Thread类,我们可以创建2种线程,分为守护线程和用户线程。

守护线程是所有非守护线程的保姆,当所有非守护线程执行完成或退出了,即使还有守护线程在运行,JVM也会直接退出,因此守护线程通常是用来处理一些辅助工作

反之,对于非守护线程,只要有一个在运行,JVM就不会退出。

典型的守护线程如垃圾回收GC线程,当用户线程都结束后,GC也就没有单独存在的必要,JVM直接退出。

1.守护线程概念

专门用于服务其他的线程,如果其他的线程(即用户自定义线程)都执行完毕,连main线程也执行完毕,那么jvm就会退出(即停止运行)。典型的守护线程就是垃圾回收线程。可以通过调用Thead类的setDaemon(true)方法设置当前的线程为守护线程。

2.守护线程使用事项

(1)setDaemon(true)必须在start()方法前执行,否则会抛IllegalThreadStateException异常;

(2)在守护线程中产生的新线程也是守护线程 ;

(3)不是所有的任务都可以分配给守护线程来执行,比如读写操作或者计算逻辑。

例如: 

public class DaemonThread extends Thread {
 
    private int i = 0;
 
    @Override
    public void run() {
        super.run();
        try {
            while (true){
                i++;
                System.out.println("i = " + i);
                Thread.sleep(1000);
            }
        }catch (Exception e){
            e.printStackTrace();
        }
    }
}
 
 
public class DaemonThread extends Thread {
 
    private int i = 0;
 
    @Override
    public void run() {
        super.run();
        try {
            while (true){
                i++;
                System.out.println("i = " + i);
                Thread.sleep(1000);
            }
        }catch (Exception e){
            e.printStackTrace();
        }
    }
}

3.守护线程属性继承父线程实例 

直接看 Thread 源代码构造方法里面就可以知道,代码如下:

private Thread(ThreadGroup g, Runnable target, String name,
 
long stackSize, AccessControlContext acc,
 
boolean inheritThreadLocals) {
 
...省略一堆代码
 
this.daemon = parent.isDaemon();
 
...省略一堆代码
 
}

4. 守护线程使用示例 

通过Thread对象的setDaemon(boolean on)方法设置是否为守护线程,要在start之前设置:

Thread thread = new Thread(runnable);
thread.setDaemon(true); // true表示守护线程,false表示用户线程
thread.start();

需要注意的是,如果没有显示调用setDaemon方法进行设置,线程的模式是取决于父线程是否为守护线程,也就是创建此线程所在的线程。

  1. 如果父线程是守护线程,创建的线程默认是守护线程;
  2. 如果父线程是用户线程,创建的线程默认是用户线程。

这可以从Thread类的init方法源代码中看出:

Thread parent = currentThread();
this.daemon = parent.isDaemon();

对于daemon的设置,保存在了Thread对象的成员变量中,Thread提供了setter/getter

private boolean daemon = false;		//	是否为守护线程

public final void setDaemon(boolean on) {
    //	SecurityManager安全检查,本文不展开讨论
    checkAccess();
	//	检查线程是否已启动,已启动无法设置daemon
    if (isAlive()) {
        throw new IllegalThreadStateException();
    }
    daemon = on;
}

public final boolean isDaemon() {
    return daemon;
}

setDaemon方法中通过isAlive判断线程是否已启动,已启动状态下不允许修改,抛出IllegalThreadStateException异常。

接着我们用示例来验证一下守护线程和非守护线程的区别。

二、守护线程示例

守护线程示例:

Thread t = new Thread(() -> {
    System.out.println("before");
    ThreadUtil.sleep(5000);
    System.out.println("after");
});
//	显式设置daemon为true
t.setDaemon(true);
t.start();

ThreadUtil.sleep(1000);
System.out.println("exit");

输出:

before
exit

可以发现,当线程设置为守护线程后,主线程一旦执行完毕,程序退出,守护线程也随着立即终止

以下是非守护线程示例:

Thread t = new Thread(() -> {
    System.out.println("before");
    ThreadUtil.sleep(5000);
    System.out.println("after");
});
//	显式设置daemon为false
t.setDaemon(false);
t.start();

ThreadUtil.sleep(1000);
System.out.println("exit");

输出:

before
exit
after

虽然主线程已经执行完毕,但创建的非守护线程还在运行

具体JVM是如何通过daemon字段控制线程的,这在JDK中找不到相应源码,需要深入hotspot C++源码进行分析。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值