淺談java中守護(hù)線程與用戶線程
Java線程分為兩類分別為daemon線程(守護(hù)線程)和User線程(用戶線程),在JVM啟動時候會調(diào)用main函數(shù),main函數(shù)所在的線程是一個用戶線程,這個是我們可以看到的線程,其實(shí)JVM內(nèi)部同時還啟動了好多守護(hù)線程,比如垃圾回收線程。那么守護(hù)線程和用戶線程有什么區(qū)別那?區(qū)別之一是當(dāng)最后一個非守護(hù)線程結(jié)束時候,JVM會正常退出,而不管當(dāng)前是否有守護(hù)線程,也就是說守護(hù)線程是否結(jié)束并不影響JVM的退出。言外之意是只要有一個用戶線程還沒結(jié)束正常情況下JVM就不會退出。
那么Java中如何創(chuàng)建一個守護(hù)線程那?代碼如下:
public static void main(String[] args) { Thread daemonThread = new Thread(new Runnable() { public void run() { } }); //設(shè)置為守護(hù)線程 daemonThread.setDaemon(true); daemonThread.start(); }
可知只需要設(shè)置線程的daemon參數(shù)為true即可。
下面通過例子來加深用戶線程與守護(hù)線程的區(qū)別的理解,首先看下面代碼:
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"); }
結(jié)果輸出為:
如上代碼在main線程中創(chuàng)建了一個thread線程,thread線程里面是無限循環(huán),運(yùn)行代碼從結(jié)果看main線程已經(jīng)運(yùn)行結(jié)束了,那么JVM進(jìn)行已經(jīng)退出了?從IDE的輸出結(jié)果右側(cè)上的紅色方塊說明JVM進(jìn)程并沒有退出,另外
mac上執(zhí)行ps -eaf | grep java會輸出結(jié)果,也可以證明這個結(jié)論。
這個結(jié)果說明了當(dāng)父線程結(jié)束后,子線程還是可以繼續(xù)存在的,也就是子線程的生命周期并不受父線程的影響。也說明了當(dāng)用戶線程還存在的情況下JVM進(jìn)程并不會終止。那么我們把上面的thread線程設(shè)置為守護(hù)線程后在運(yùn)行看看會有什么效果:
//設(shè)置為守護(hù)線程 thread.setDaemon(true); //啟動子線 thread.start();
執(zhí)行結(jié)果為:
如上在啟動線程前設(shè)置線程為守護(hù)線程,從輸出結(jié)果可知JVM進(jìn)程已經(jīng)終止了,執(zhí)行ps -eaf |grep java 也看不到JVM進(jìn)程了。這個例子里面main函數(shù)是唯一的用戶線程,thread線程是守護(hù)線程,當(dāng)main線程運(yùn)行結(jié)束后,JVM發(fā)現(xiàn)當(dāng)前已經(jīng)沒有用戶線程了,就會終止JVM進(jìn)程。
Java中在main線程運(yùn)行結(jié)束后,JVM會自動啟動一個叫做DestroyJavaVM線程,該線程會等待所有用戶線程結(jié)束后終止JVM進(jìn)程,下面通過簡單的JVM代碼來證明這個結(jié)論:
翻開JVM的代碼,最終會調(diào)用到JavaMain這個c函數(shù)
int JNICALL JavaMain(void * _args) { ... //執(zhí)行Java中的main函數(shù) (*env)->CallStaticVoidMethod(env, mainClass, mainID, mainArgs); //main函數(shù)返回值 ret = (*env)->ExceptionOccurred(env) == NULL ? 0 : 1; //等待所有非守護(hù)線程結(jié)束,然后銷毀JVM進(jìn)程 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)
上面宏的作用實(shí)際是創(chuàng)建了一個名字叫做DestroyJavaVM的線程來等待所有用戶線程結(jié)束。
總結(jié):如果你想在主線程結(jié)束后JVM進(jìn)程馬上結(jié)束,那么創(chuàng)建線程的時候可以設(shè)置線程為守護(hù)線程,否者如果希望主線程結(jié)束后子線程繼續(xù)工作,等子線程結(jié)束后在讓JVM進(jìn)程結(jié)束那么就設(shè)置子線程為用戶線程,開源框架Tomcat中就是用了守護(hù)線程和用戶線程聯(lián)合運(yùn)行起來的,具體敬請期待Java并發(fā)編程基礎(chǔ)之并發(fā)包源碼剖析一書出版。
以上就是本文的全部內(nèi)容,希望對大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
相關(guān)文章
IntelliJ-Idea導(dǎo)出可執(zhí)行Jar流程解析
這篇文章主要介紹了IntelliJ-Idea導(dǎo)出可執(zhí)行Jar流程解析,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友可以參考下2020-12-12詳解SpringMVC的類型轉(zhuǎn)換及驗(yàn)證方法
在本篇文章里面我們給大家詳細(xì)分析了SpringMVC的類型轉(zhuǎn)換及驗(yàn)證方法的相關(guān)知識,對此有需要的朋友們學(xué)習(xí)下吧。2018-10-10jpa實(shí)現(xiàn)多對多的屬性時查詢的兩種方法
這篇文章主要介紹了jpa實(shí)現(xiàn)多對多的屬性時查詢的兩種方法,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2021-11-11springboot使用Hutool的JschUtil及下載安裝步驟
這篇文章主要為大家介紹了springboot使用Hutool的JschUtil的方法及下載安裝詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-08-08JDBC 實(shí)現(xiàn)通用的增刪改查基礎(chǔ)類方法
下面小編就為大家分享一篇JDBC 實(shí)現(xiàn)通用的增刪改查基礎(chǔ)類方法,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧2018-01-01