专栏首页Java编程技术java中守护线程与用户线程

java中守护线程与用户线程

Java线程分为两类分别为daemon线程(守护线程)和User线程(用户线程),在JVM启动时候会调用main函数,main函数所在的线程是一个用户线程,这个是我们可以看到的线程,其实JVM内部同时还启动了好多守护线程,比如垃圾回收线程。那么守护线程和用户线程有什么区别那?区别之一是当最后一个非守护线程结束时候,JVM会正常退出,而不管当前是否有守护线程,也就是说守护线程是否结束并不影响JVM的退出。言外之意是只要有一个用户线程还没结束正常情况下JVM就不会退出。

那么Java中如何创建一个守护线程那?代码如下:

public static void main(String[] args) {

        Thread daemonThread = new Thread(new  Runnable() {
            public void run() {
                
            }
        });
        
        //设置为守护线程
        daemonThread.setDaemon(true);
        daemonThread.start();
        
    } 

可知只需要设置线程的daemon参数为true即可。

下面通过例子来加深用户线程与守护线程的区别的理解,首先看下面代码:

    public static void main(String[] args) {

        Thread thread = new Thread(new  Runnable() {
            public void run() {
                for(;;){}
            }
        });
        
        //启动子线
        thread.start();
        
        System.out.print("main thread is over");
    }

结果输出为:

image.png

如上代码在main线程中创建了一个thread线程,thread线程里面是无限循环,运行代码从结果看main线程已经运行结束了,那么JVM进行已经退出了?从IDE的输出结果右侧上的红色方块说明JVM进程并没有退出,另外 mac上执行ps -eaf | grep java会输出结果,也可以证明这个结论。

这个结果说明了当父线程结束后,子线程还是可以继续存在的,也就是子线程的生命周期并不受父线程的影响。也说明了当用户线程还存在的情况下JVM进程并不会终止。那么我们把上面的thread线程设置为守护线程后在运行看看会有什么效果:

        //设置为守护线程
        thread.setDaemon(true);
        //启动子线
        thread.start();

执行结果为:

image.png

如上在启动线程前设置线程为守护线程,从输出结果可知JVM进程已经终止了,执行ps -eaf |grep java 也看不到JVM进程了。这个例子里面main函数是唯一的用户线程,thread线程是守护线程,当main线程运行结束后,JVM发现当前已经没有用户线程了,就会终止JVM进程。

Java中在main线程运行结束后,JVM会自动启动一个叫做DestroyJavaVM线程,该线程会等待所有用户线程结束后终止JVM进程,下面通过简单的JVM代码来证明这个结论:

翻开JVM的代码,最终会调用到JavaMain这个c函数

int JNICALL
JavaMain(void * _args)
{   
    ...
    //执行Java中的main函数 
    (*env)->CallStaticVoidMethod(env, mainClass, mainID, mainArgs);
    
    //main函数返回值
    ret = (*env)->ExceptionOccurred(env) == NULL ? 0 : 1;
    
    //等待所有非守护线程结束,然后销毁JVM进程
    LEAVE();
}

LEAVE是c语言里面的一个宏定义,定义如下:

#define LEAVE() \
    do { \
        if ((*vm)->DetachCurrentThread(vm) != JNI_OK) { \
            JLI_ReportErrorMessage(JVM_ERROR2); \
            ret = 1; \
        } \
        if (JNI_TRUE) { \
            (*vm)->DestroyJavaVM(vm); \
            return ret; \
        } \
    } while (JNI_FALSE)

上面宏的作用实际是创建了一个名字叫做DestroyJavaVM的线程来等待所有用户线程结束。

总结:如果你想在主线程结束后JVM进程马上结束,那么创建线程的时候可以设置线程为守护线程,否者如果希望主线程结束后子线程继续工作,等子线程结束后在让JVM进程结束那么就设置子线程为用户线程,开源框架Tomcat中就是用了守护线程和用户线程联合运行起来的,具体敬请期待Java并发编程基础之并发包源码剖析一书出版。

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • 使用线程池时候当程序结束时候记得调用shutdown关闭线程池

    日常开发中为了便于线程的有效复用,线程池是经常会被用的工具,然而线程池使用完后如果不调用shutdown会导致线程池资源一直不会被释放。下面通过简单例子来说明该...

    加多
  • Dubbo线程模型与线程池策略

    Dubbo 默认的底层网络通讯使用的是 Netty ,服务提供方 NettyServer 使用两级线程池,其中 EventLoopGroup(boss) 主要用...

    加多
  • Dubbo剖析-线程模型

    Dubbo默认的底层网络通讯是使用Netty来做的,在服务提供方NettyServer使用两级线程池,其中EventLoopGroup(boss)主要用来接受客...

    加多
  • 线程池的作用和CLR线程池

    在程序的世界里,如果创建某种对象所需要的代价太高,同时这个对象又可以反复使用,那么我们往往就会准备一个容器,用来保存一批这样的对象。当我们要用这种对象时,就不需...

    小蜜蜂
  • JAVA多线程与并发学习总结

    使用高速缓存来作为内存与处理器之间的缓冲,将运算需要用到的数据复制到缓存中,让计算能快速进行;当运算结束后再从缓存同步回内存之中,这样处理器就无需等待缓慢的内存...

    烂猪皮
  • Java 多线程编程(“锁”事碎碎念)

    对于多个线程间的共享数据,悲观锁认为自己在使用数据的时候很有可能会有其它线程也刚好前来修改数据,因为在使用数据前都会加上锁,确保在使用过程中数据不会被其它线程修...

    叶志陈
  • Java 多线程编程(聊聊线程池)

    线程是一种昂贵的系统资源,其“昂贵”不仅在于创建线程所需要的资源开销,还在于使用过程中带来的资源消耗。一个系统能够支持同时运行的线程总数受限于该系统所拥有的处理...

    叶志陈
  • 编程体系结构(05):Java多线程并发

    线程是操作系统能够进行运算调度的最小单位,包含在进程之中,是进程中的实际运作单位。一条线程指的是进程中一个单一顺序的控制流,一个进程中可以并发多个线程,每条线程...

    知了一笑
  • 新手一看就懂的线程池

    线程池是帮助我们管理线程的工具,它维护了多个线程,可以降低资源的消耗,提高系统的性能。

    好好学java

扫码关注云+社区

领取腾讯云代金券