說說用戶線程和守護線程
用戶線程和守護線程了解嗎?
什麼是用戶線程和守護線程?
守護線程是一種特殊的線程,在後台默默地完成一些系統性的服務,比如垃圾回收線程、JIT線程都是守護線程。與之對應的是用戶線程,用戶線程可以理解為是系統的工作線程,它會完成這個程序需要完成的業務操作。
如何手動設置線程為守護線程?
java 中的線程分為兩種:守護線程(Daemon)和用戶線程(User)。任何線程都可以設置為守護線程和用戶線程,通過方法 setDaemon() 即可實現。
// 接口方法
void setDaemon(boolean on) // 將此線程標記為用戶線程,true 則把該線程設置為守護線程,反之則為用戶線程
boolean isDaemon() // 判斷這個線程是否是守護線程,返回true表示守護線程,否則為用戶線程
注意點:
- 當程序中所有的用戶線程執行完畢之後,不管守護線程是否結束,系統都會自動退出;
- java線程分為用戶線程和守護線程,線程的 daemon 屬性為 true 表示是守護線程,false 表示是用戶線程;
- 設置守護線程:
t1.setDaemon(true)
,該語句要放在t1.start()
方法執行之前,如果放在後面,會報IllegalThreadStateException
異常,不起作用;
🌰 1:
public class Demo {
/**
驗證:當程序中所有的用戶線程執行完畢之後,不管守護線程是否結束,系統都會自動退出
*/
static class T1 extends Thread {
public T1(String name) {
super(name);
}
@Override
public void run() {
System.out.println(this.getName() + "開始執行," + (this.isDaemon() ? "我是守護線程" : "我是用戶線程"));
while (true) ; // 死循環
}
}
public static void main(String[] args) {
T1 t1 = new T1("t1");
t1.setDaemon(true); // 放在 t1.start() 方法執行之前,設為守護線程
t1.start();
System.out.println("主線程結束");
}
}
//------------------ 運行結果
主線程結束
t1開始執行,我是守護線程 (程序正常結束)
🌰 2:
/**
驗證:設置守護線程,需要在start()方法之前進行
*/
public class Demo1 {
static void main(String[] args) {
Thread t1 = new Thread() {
@Override
public void run() {
try {
TimeUnit.SECONDS.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
};
t1.start();
t1.setDaemon(true);
}
}
//------------------ 運行結果
Exception in thread "main" java.lang.IllegalThreadStateException
at java.lang.Thread.setDaemon(Thread.java:1359)
源碼看看
查看 Thread 的源碼,發現 布爾型變量 daemon 是Thread類中的一個成員變量,默認值為 false。
/* Thread.java */
public class Thread implements Runnable {
......
// 布爾型變量 daemon 是Thread類中的一個成員變量,默認值為 false
private boolean daemon = false;
}
查看線程的 init
方法(init 方法在創建線程時會執行,該方法作用在Thread
構造器內部),可以看出 dameon 的默認值為父線程的daemon。也就是說,父線程如果為用戶線程,子線程默認也是用戶現場;父線程如果是守護線程,子線程默認也是守護線程。
父線程就是創建當前線程的那個線程,比如我們經常在main函數中 new 一個 t1 線程,那 main 線程是 t1 線程的父線程,t1 是子線程。
/* Thread.java */
private void init(ThreadGroup g, Runnable target, String name,.......){
......
Thread parent = currentThread();
this.daemon = parent.isDaemon();
......
}
🌰 1:
public class Demo2 {
static class T extends Thread {
public T(String name) {
super(name);
}
@Override
public void run() {
System.out.println(this.getName() + ".daemon:" + this.isDaemon());
}
}
public static void main(String[] args) throws InterruptedException {
// 打印 main 線程的狀態
System.out.println(Thread.currentThread().getName() + ".daemon:" + Thread.currentThread().isDaemon());
// main 線程中創建了 t1 線程
T t1 = new T("t1");
t1.start();
Thread t2 = new Thread() {
@Override
public void run() {
System.out.println(this.getName() + ".daemon:" + this.isDaemon());
// t2 內創建了 t3 線程
T t3 = new T("t3");
t3.start();
}
};
t2.setName("t2");
t2.setDaemon(true); // 手動設置 t2 為守護線程
t2.start();
TimeUnit.SECONDS.sleep(2);
}
}
//------------------ 運行結果
main.daemon:false
t1.daemon:false
t2.daemon:true
t3.daemon:true
總結
- java 中的線程分為兩種:守護線程(Daemon)和用戶線程(User);
- 當程序中所有的用戶線程執行完畢之後,不管守護線程是否結束,系統都會自動退出;
- 任何線程都可以設置為守護線程和用戶線程,通過方法
setDaemon(boolean on)
即可實現。此外,setDaemon(boolean on)
方法必須在線程的start()方法之前調用,在後面調用會報異常; - 線程的
daemon
默認值和其父線程(創建它的線程)一樣; - Thread Dump 打印出來的線程信息,含有 daemon 字樣的線程即為守護進程,常見的守護線程有 服務守護進程、編譯守護進程、windows 下的監聽 Ctrl+break 的守護進程、Finalizer 守護進程、引用處理守護進程、GC 守護進程。