Java中的守護(hù)線程問(wèn)題
守護(hù)線程
在Java中有兩類線程
- User Thread(用戶線程)
- Daemon Thread(守護(hù)線程)
守護(hù)線程的功能非常簡(jiǎn)單,在其本身是一個(gè)線程的同時(shí),主要是為了給其他的線程提供服務(wù),比如說(shuō)計(jì)時(shí)器,清空高速緩存等等操作,守護(hù)線程具有和被守護(hù)線程一樣的生命周期(這里并不是說(shuō)守護(hù)線程和被守護(hù)線程常常是1-1的關(guān)系),當(dāng)被守護(hù)線程死亡,守護(hù)線程往往也會(huì)死亡,當(dāng)虛擬機(jī)中只剩下守護(hù)線程時(shí),虛擬機(jī)就會(huì)退出,因?yàn)榇藭r(shí)也沒(méi)有運(yùn)行程序的必要了
一個(gè)比較通俗的解釋:任何一個(gè)守護(hù)線程都是整個(gè)JVM中所有非守護(hù)線程的保姆
只要當(dāng)前JVM實(shí)例中尚存在任何一個(gè)非守護(hù)線程沒(méi)有結(jié)束,守護(hù)線程就全部工作;只有當(dāng)最后一個(gè)非守護(hù)線程結(jié)束時(shí),守護(hù)線程隨著JVM一同結(jié)束工作。
守護(hù)線程的作用是為其他線程的運(yùn)行提供便利服務(wù),守護(hù)線程最典型的應(yīng)用就是 GC (垃圾回收器),它就是一個(gè)很稱職的守護(hù)者。
需要注意的點(diǎn)是:
- 守護(hù)線程的優(yōu)先級(jí)比較低
- 守護(hù)線程要注意考慮關(guān)機(jī)動(dòng)作
- 守護(hù)線程應(yīng)該永遠(yuǎn)不去訪問(wèn)固有資源,比如說(shuō)文件或者數(shù)據(jù)庫(kù),因?yàn)樗鼤?huì)在任何時(shí)候甚至一個(gè)操作的中間發(fā)生中斷。
不要給守護(hù)線程分擔(dān)讀寫邏輯或者計(jì)算邏輯,因?yàn)闊o(wú)法確定守護(hù)線程是否已經(jīng)完成了工作,但是只要User退出守護(hù)線程也會(huì)立馬結(jié)束,對(duì)于計(jì)算機(jī)程序來(lái)說(shuō)這樣的程序可能多次運(yùn)行結(jié)果不一樣,很顯然這對(duì)于程序來(lái)說(shuō)是毀滅性的。
操作:
通過(guò)thread.setDaemon(true) 將線程轉(zhuǎn)換為守護(hù)線程
這個(gè)方法必須在thread.start()之前進(jìn)行調(diào)用
守護(hù)線程與用戶線程的區(qū)別
介紹Java守護(hù)線程與用戶線程的概念和使用方法,以及相關(guān)注意事項(xiàng)。
1 定義和概述
Java 中的線程分為兩類,分別為daemon 線程(守護(hù)線程〉和user 線程(用戶線程)。守護(hù)線程又稱Daemon線程,運(yùn)行在后臺(tái),看不見(jiàn);用戶線程運(yùn)行在前臺(tái),看的見(jiàn)。
在JVM啟動(dòng)時(shí)會(huì)調(diào)用main 函數(shù), main 函數(shù)所在的線程就是一個(gè)用戶線程,其實(shí)在JVM內(nèi)部同時(shí)-還啟動(dòng)了好多守護(hù)線程, 比如垃圾回收線程。
Daemon線程是一種支持型線程,因?yàn)樗饕挥米鞒绦蛑泻笈_(tái)調(diào)度以及支持性工作。這意味著,當(dāng)一個(gè)Java虛擬機(jī)中不存在非Daemon線程的時(shí)候,Java虛擬機(jī)將會(huì)退出,而不管當(dāng)前是否有守護(hù)線程,也就是說(shuō)守護(hù)線程是否結(jié)束并不影響JVM的退出。
實(shí)際上,在main線程運(yùn)行結(jié)束后,JVM會(huì)自動(dòng)啟動(dòng)一個(gè)叫作DestroyJavaVM 的線程,該線程會(huì)等待所有用戶線程結(jié)束后終止JVM 進(jìn)程。
在Tomcat的NIO實(shí)現(xiàn)NioEndpoint中會(huì)開啟一組接受線程來(lái)接受用戶的連接請(qǐng)求,以及一組處理線程負(fù)責(zé)具體處理用戶請(qǐng)求,在默認(rèn)情況下,接受線程和處理線程都是守護(hù)線程,這意味著當(dāng)tomcat 收到shutdown 命令后并且沒(méi)有其他用戶線程存在的情況下tomcat 進(jìn)程會(huì)馬上消亡,而不會(huì)等待處理線程處理完當(dāng)前的請(qǐng)求。
2 使用守護(hù)線程
在線程start之前,可以通過(guò)調(diào)用thread.setDaemon(true)將線程設(shè)置為Daemon線程。
守護(hù)線程有兩種結(jié)束方式:
- 守護(hù)線程也具有自己的run();方法,當(dāng)后臺(tái)線程完成自己的run方法后,守護(hù)線程結(jié)束。
- 用戶線程運(yùn)行結(jié)束,守護(hù)線程自動(dòng)結(jié)束。
3 測(cè)試案例
public class Daemon {
//啟動(dòng)該類,將會(huì)構(gòu)造兩條線程,main線程和一條子線程。
public static void main(String[] args) throws InterruptedException {
//測(cè)試非守護(hù)線程
//可以看到,輸出"main線程結(jié)束"之后,子線程還在繼續(xù)輸出,程序沒(méi)有結(jié)束
// test1();
//測(cè)試守護(hù)線程
//可以看到,輸出"main線程結(jié)束"之后,子線程沒(méi)有繼續(xù)輸出,程序結(jié)束
test2();
}
/**
* 測(cè)試非守護(hù)線程
*
* @throws InterruptedException
*/
public static void test1() throws InterruptedException {
Thread thread = new Thread(() -> {
while (true) {
try {
Thread.currentThread().sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("子線程非守護(hù)線程");
}
});
thread.start();
Thread.currentThread().sleep(1000);
System.out.println("main線程結(jié)束");
}
//測(cè)試守護(hù)線程
public static void test2() throws InterruptedException {
Thread thread = new Thread(() -> {
while (true) {
try {
Thread.currentThread().sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("子線程守護(hù)線程");
}
});
thread.setDaemon(true);
thread.start();
Thread.currentThread().sleep(1000);
System.out.println("main線程結(jié)束");
}
}使用jps查看java進(jìn)程,可以發(fā)現(xiàn)如果子線程是守護(hù)線程那么主線程結(jié)束,子線程也結(jié)束了;如果子線程不是守護(hù)線程那么主線程結(jié)束,子線程沒(méi)有結(jié)束。

4 注意事項(xiàng)
Daemon線程被用作完成支持性工作,但是在Java虛擬機(jī)退出時(shí)Daemon線程中的finally塊并不一定會(huì)執(zhí)行,如下代碼:
public class Daemon {
public static void main(String[] args) {
Thread thread = new Thread(new DaemonRunner(), "DaemonRunner");
thread.setDaemon(true);
thread.start();
}
static class DaemonRunner implements Runnable {
@Override
public void run() {
try {
SleepUtils.second(10);
} finally {
System.out.println("DaemonThread finally run.");
}
}
}
}運(yùn)行Daemon程序,可以看到在控制臺(tái)上沒(méi)有任何輸出。main線程(非Daemon線程)在啟動(dòng)了線程DaemonRunner之后隨著main方法執(zhí)行完畢而終止,而此時(shí)Java虛擬機(jī)中已經(jīng)沒(méi)有非Daemon線程,虛擬機(jī)需要退出。Java虛擬機(jī)中的所有Daemon線程都需要立即終止,因此DaemonRunner立即終止,但是DaemonRunner中的finally塊并沒(méi)有執(zhí)行。
在構(gòu)建Daemon線程時(shí),不能依靠finally塊中的內(nèi)容來(lái)確保執(zhí)行關(guān)閉或清理資源的邏輯。
以上為個(gè)人經(jīng)驗(yàn),希望能給大家一個(gè)參考,也希望大家多多支持腳本之家。
相關(guān)文章
Eclipse下編寫java程序突然不會(huì)自動(dòng)生成R.java文件和包的解決辦法
這篇文章主要介紹了Eclipse下編寫java程序突然不會(huì)自動(dòng)生成R.java文件和包的解決辦法 的相關(guān)資料,需要的朋友可以參考下2016-01-01
解決Maven本地倉(cāng)庫(kù)明明有對(duì)應(yīng)的jar包但還是報(bào)找不到的問(wèn)題
這篇文章主要介紹了解決Maven本地倉(cāng)庫(kù)明明有對(duì)應(yīng)的jar包但還是報(bào)找不到的問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2022-10-10
帶你了解Java數(shù)據(jù)結(jié)構(gòu)和算法之無(wú)權(quán)無(wú)向圖
這篇文章主要為大家介紹了Java數(shù)據(jù)結(jié)構(gòu)和算法之無(wú)權(quán)無(wú)向圖?,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下,希望能夠給你帶來(lái)幫助2022-01-01
詳解java調(diào)用存儲(chǔ)過(guò)程并封裝成map
這篇文章主要介紹了詳解java調(diào)用存儲(chǔ)過(guò)程并封裝成map的相關(guān)資料,希望通過(guò)本文能幫助到大家實(shí)現(xiàn)這樣的功能,需要的朋友可以參考下2017-09-09
Java?Stream函數(shù)式編程管道流結(jié)果處理
這篇文章主要為大家介紹了Java?Stream函數(shù)式編程管道流結(jié)果處理的示例過(guò)程解析需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步2022-03-03

