Java併發基礎(八)——線程組(ThreadGroup)與守護線程(Daemon)

線程組(ThreadGroup)

        在一個系統中,如果線程數量很多,而且功能分配比較明確,就可以將相同功能的線程放置在同一個線程組裏。下面通過示例來說明。

public class ThreadGroupName implements Runnable {

    public static void main(String[] args){
        ThreadGroup tg = new ThreadGroup("PringGroup");
        Thread t1 = new Thread(tg, new ThreadGroupName(), "T1");
        Thread t2 = new Thread(tg, new ThreadGroupName(), "T2");

        t1.start();
        t2.start();

        System.out.println(tg.activeCount());
        tg.list();
    }


    @Override
    public void run(){
        String groupAndName = Thread.currentThread().getThreadGroup().getName() + "-" + Thread.currentThread().getName();
        while (true) {
            System.out.println("I am " + groupAndName);
            try {
                Thread.sleep(3000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

        上述代碼中,第4行建立了一個名爲“PrintGroup”的線程組,並將T1和T2兩個線程加入到此線程組中。第5、6兩行創建了兩個線程,使用Thread的構造函數,指定了線程所屬的線程組,將線程和線程組關聯起來。activeCount()方法可以獲得活動線程的總數。不過線程是動態的,因此這個值只是一個估計值,無法精確。list()方法打印這個線程組中所有的線程信息,對調試有一定幫助。打印結果如下:

        線程組還有一個值得注意的方法stop(),它會停止線程組中所有的線程。看起來是一個很方便的方法,但它會遇到和Thread.stop()方法同樣的問題,因此使用時需要格外謹慎。

        另外,提一下編碼習慣。在創建線程和線程組的時候,建議取一個好聽名字。對計算機來說不重要,但在系統出現問題時,很可能需要知道系統內的所有線程,如果拿到的是一連串的Thread-0、Thread-1、Thread-2,一定會頭疼。而如果看到的是類似HttpHandler、FTPService這樣的名字,則會順心很多。

 

守護線程(Daemon)

        守護線程是一種特殊的線程,就像它的名字一樣,它是系統的守護者。在後臺默默的完成一些系統性的服務,比如垃圾回收線程、JIT線程就可以理解爲守護線程。與之相應的是用戶線程,用戶線程可以認爲是系統的工作線程,它會完成這個程序應該要完成的業務操作。如果用戶線程全部結束,則意味着這個程序實際上無事可做了。守護線程要守護的對象已經不存在了,那麼整個應用程序就應該結束。因此一個Java應用內只有守護線程時,Java虛擬機就會自然退出

        下面看一個簡單的守護線程的應用。

public class DaemonDemo {

    public static void main(String[] args) throws InterruptedException {
        Thread t = new DaemonT();
        // 將線程t設置爲守護線程,注意這一步要在start()方法之前設置
        t.setDaemon(true);
        t.start();

        Thread.sleep(2000);
    }


    public static class DaemonT extends Thread{
        @Override
        public void run(){
            while (true){
                System.out.println("I am alive");
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e){
                    e.printStackTrace();
                }
            }
        }
    }
}

        上述代碼中,第6行將線程t設置爲守護線程。需要注意的是,設置守護線程必須在start()方法之前設置。否則出拋出下面的異常,告訴你守護線程設置失敗。不過程序和線程依然可以正常執行,從打印的結果中也看到了,還是打印了“I am alive”。只不過被當作用戶線程而已。因此如果不小心忽略了下面的異常信息,很可能察覺不到這個錯誤,就會詫異爲什麼程序永遠停不下來呢?

        在這個例子中,由於線程t被設置爲守護線程,系統中只有主線程main爲用戶線程,因此在main線程休眠2秒鐘後退出時,整個程序也隨之結束了。但如果不把線程t設置爲守護線程,那main線程結束後,t線程還會不停地打印,永遠也不會結束。正確的結果打印如下:

 

注:以上內容參考《實戰Java高併發程序設計(第2版)》。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章