說說用戶線程和守護線程

用戶線程和守護線程了解嗎?

什麼是用戶線程和守護線程?

守護線程是一種特殊的線程,在後台默默地完成一些系統性的服務,比如垃圾回收線程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

總結

  1. java 中的線程分為兩種:守護線程(Daemon)和用戶線程(User);
  2. 當程序中所有的用戶線程執行完畢之後,不管守護線程是否結束,系統都會自動退出;
  3. 任何線程都可以設置為守護線程和用戶線程,通過方法 setDaemon(boolean on) 即可實現。此外,setDaemon(boolean on)方法必須在線程的start()方法之前調用,在後面調用會報異常;
  4. 線程的daemon默認值和其父線程(創建它的線程)一樣;
  5. Thread Dump 打印出來的線程信息,含有 daemon 字樣的線程即為守護進程,常見的守護線程有 服務守護進程、編譯守護進程、windows 下的監聽 Ctrl+break 的守護進程、Finalizer 守護進程、引用處理守護進程、GC 守護進程。

參考

//itsoku.com/course/1/9#目錄