文章出處

在正式理解這個概念前,先把 守護程 與 守護程 這二個極其相似的說法區分開,守護進程通常是為了防止某些應用因各種意外原因退出,而在后臺獨立運行的系統服務或應用程序。 比如:我們開發了一個郵件發送程序,一直不停的監視隊列池,發現有待發送的郵件,就將其發送出去。如果這個程序掛了(或被人誤操作關了),郵件就不發出去了,為了防止這種情況,再開發一個類似windows 系統服務的應用,常駐后臺,監制這個郵件發送程序是否在運行,如果沒運行,則自動將其啟動。

 

而我們今天說的java中的守護程(Daemon Thread) 指的是一類特殊的Thread,其優先級特別低(低到甚至可以被JVM自動終止),通常這類線程用于在空閑時做一些資源清理類的工作,比如GC線程,如果JVM中所有非守護線程(即:常規的用戶線程)都結束了,守護線程會被JVM中止,想想其實也挺合理,沒有任何用戶線程了,自然也不會有垃圾對象產生,GC線程也沒必要存在了。

 

實際開發中,也可以手動將線程設置為Daemon Thread,只有一個限制:必須在線程的start方法設置,見下面的示例:

package test;

public class Program {

    public static void main(String[] args) {
        TestThread t1 = new TestThread();
        t1.setDaemon(true);
        t1.start();
    }

    private static class TestThread extends Thread {
        public void run() {
            System.out.println("test");
        }
    }
}

由于t1設置成Daemon Thread了,運行后,main進程馬上就結束,此時沒有用戶進程在運行,守護進程默認是不執行的,因此運行后,沒有任何輸出結果,符合我們剛才的解釋。

注:在idea等集成IDE環境下測試時,如果多次點擊Run按鈕,可能會發現第二次運行時,偶爾也會輸出test,估計是ide里上次運行后的java進程并未完全退出,可以手動把windows進程中的所有java.exe進程干掉再測試。

如果把t1.setDaemon(true);這一行注釋掉,就會輸出test了。

另外,如果把main函數最后加一行阻塞的代碼,比如:

    public static void main(String[] args) throws IOException {
        TestThread t1 = new TestThread();
        t1.setDaemon(true);
        t1.start();
        System.in.read();
    }

加了一行System.in.read()后,再運行,會發現test會輸出,這是因為main這個用戶線程被阻塞了,JVM發現有用戶進程在運行,守護進程才能機會被執行。

 

再來一個復雜點的示例:

假設有二個線程,一個是常規的用戶線程,不停寫入日志,另一個是守護線程,在空閑時清理日志(僅保留最近的5條日志)

package test;

import java.io.IOException;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue;

public class Program {

    private static int queueCapacity = 10;
    private static BlockingQueue<String> logQueue = new ArrayBlockingQueue<String>(queueCapacity);

    public static void main(String[] args) throws IOException {

        LogWriter writer = new LogWriter();
        LogCleaner cleaner = new LogCleaner();
        cleaner.setDaemon(true);

        writer.start();
        cleaner.start();
    }

    /**
     * 模擬不停寫日志(直到隊列寫滿)
     */
    private static class LogWriter extends Thread {
        public void run() {
            for (int i = 0; i < queueCapacity; i++) {
                try {
                    logQueue.put("" + i);
                    System.out.println("日志已寫入,當前日志內容:" + logQueue);
                    Thread.sleep(500);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
    }


    /**
     * 模擬在空閑時清理日志(僅保留5條日志)
     */
    private static class LogCleaner extends Thread {
        public void run() {
            while (true) {
                if (logQueue.size() > 5) {
                    try {
                        logQueue.take();
                        System.out.println("多余日志被清理,當前日志內容:" + logQueue);
                        Thread.sleep(500);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }
        }
    }
}

運行結果:

 1 日志已寫入,當前日志內容:[0]
 2 日志已寫入,當前日志內容:[0, 1]
 3 日志已寫入,當前日志內容:[0, 1, 2]
 4 日志已寫入,當前日志內容:[0, 1, 2, 3]
 5 日志已寫入,當前日志內容:[0, 1, 2, 3, 4]
 6 日志已寫入,當前日志內容:[0, 1, 2, 3, 4, 5]
 7 多余日志被清理,當前日志內容:[1, 2, 3, 4, 5]
 8 日志已寫入,當前日志內容:[1, 2, 3, 4, 5, 6]
 9 多余日志被清理,當前日志內容:[2, 3, 4, 5, 6]
10 日志已寫入,當前日志內容:[2, 3, 4, 5, 6, 7]
11 多余日志被清理,當前日志內容:[3, 4, 5, 6, 7]
12 日志已寫入,當前日志內容:[3, 4, 5, 6, 7, 8]
13 多余日志被清理,當前日志內容:[4, 5, 6, 7, 8]
14 日志已寫入,當前日志內容:[4, 5, 6, 7, 8, 9]
15 多余日志被清理,當前日志內容:[5, 6, 7, 8, 9]

 

參考文章:

http://ifeve.com/thread-management-8/

http://www.cnblogs.com/super-d2/p/3348183.html


文章列表




Avast logo

Avast 防毒軟體已檢查此封電子郵件的病毒。
www.avast.com


arrow
arrow
    全站熱搜
    創作者介紹
    創作者 大師兄 的頭像
    大師兄

    IT工程師數位筆記本

    大師兄 發表在 痞客邦 留言(0) 人氣()