java中守護線程的一些概念和用法


網上的資料中,守護線程的功能一般都是“只要當前JVM實例中尚存任何一個非守護線程沒有結束,守護線程就全部工作;只有當最后一個非守護線程結束是,守護線程隨着JVM一同結束工作,Daemon作用是為其他線程提供便利服務,守護線程最典型的應用就是GC(垃圾回收器),他就是一個很稱職的守護者。”
可是,我發現真實情況卻不是描述的這么回事,因為我對java也不懂,所以在此記錄一下守護線程中的一些問題。

我的思路是:
在main線程中建立兩個線程,一個線程A只打印一行信息就退出,它被設置成守護線程,另一個線程B通過sleep和循環來進行時間控制,讓它多運行一會,他被設置成非守護線程。如果按照上面的說明,我認為線程A怎么說也要和線程B的消亡時間是一樣的。實際情況卻不是這樣的。
下面是代碼:

文件 ThreadDaemon.java

package javaStudy.threadStudy;

import java.util.Date;

public class ThreadDaemon {
    public static void main(String[] args) {
        // Thread.currentThread().setDaemon(true);
        // Thread.currentThread().start();
        // 系統的main函數使用一個主線程,主線程不是守護線程,也不能被設置成守護線程
        System.out.println(Thread.currentThread().getName() + "\tprocess begin\t" + (new Date()) + "\t"
                + Thread.currentThread().isDaemon() + "\tthread amount\t"
                + Thread.currentThread().getThreadGroup().activeCount());
        showThreadName();
        // 新建一個線程,並將其設置成守護線程,他的操作僅僅是打印一行信息。
        Thread t = new Thread(ThreadDaemon::daemonPrint);
        t.setDaemon(true);
        t.start();
        System.out.println(Thread.currentThread().getName() + "\tafter create thread A\t" + (new Date()) + "\t"
                + Thread.currentThread().isDaemon() + "\tthread amount\t"
                + Thread.currentThread().getThreadGroup().activeCount());
        showThreadName();

        // 再建立一個線程,將其設置成用戶線程,即非守護線程。讓他多執行一會。
        try {
            Thread.sleep(1000 * 3);
            Thread thread2 = new Thread(ThreadDaemon::print);
            thread2.setDaemon(false);
            thread2.start();
            System.out.println(Thread.currentThread().getName() + "\tafter create thread B\t" + (new Date()) + "\t"
                    + Thread.currentThread().isDaemon() + "\tthread amount\t"
                    + Thread.currentThread().getThreadGroup().activeCount());
            showThreadName();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        System.out.println(Thread.currentThread().getName() + "\tExit:" + "\t" + (new Date()) + "\t"
                + Thread.currentThread().isDaemon() + "\tthread amount "
                + Thread.currentThread().getThreadGroup().activeCount());
        showThreadName();
        // System.exit(0) 是退出jvm。如果有此代碼,則子線程也會直接隨着主線程而退出。如果沒有此代碼,jvm會在子線程結束的時候而退出。
        // System.exit(0);
    }

    // 用戶線程的調用
    public static void print() {
        int counter = 1;
        while (counter < 5) {
            try {
                Thread.sleep(3 * 1000); // sleep for 10 seconds
                System.out.println(Thread.currentThread().getName() + "\tbefore Counter:" + counter++ + "\t"
                        + (new Date()) + "\t" + Thread.currentThread().isDaemon() + "\tthread amount "
                        + Thread.currentThread().getThreadGroup().activeCount());
                Thread.sleep(3 * 1000); // sleep for 10 seconds
                System.out.println(Thread.currentThread().getName() + "\tafter Counter:" + counter++ + "\t"
                        + (new Date()) + "\t" + Thread.currentThread().isDaemon() + "\tthread amount "
                        + Thread.currentThread().getThreadGroup().activeCount());
                showThreadName();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }

    // 守護線程的調用
    public static void daemonPrint() {
        try {
            Thread.sleep(6 * 1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        System.out.println(
                Thread.currentThread().getName() + "\t" + (new Date()) + "\t" + Thread.currentThread().isDaemon()
                        + "\tthread amount " + Thread.currentThread().getThreadGroup().activeCount());
        showThreadName();
    }

    // 顯示線程名稱
    public static void showThreadName() {
        ThreadGroup currentGroup = Thread.currentThread().getThreadGroup();
        int noThreads = currentGroup.activeCount();
        Thread[] lstThreads = new Thread[noThreads];
        currentGroup.enumerate(lstThreads);
        for (int i = 0; i < noThreads; i++) {
            System.out.println("==============="+lstThreads[i].getName() + "\t" + lstThreads[i].isDaemon());
        }
    }
}

以上代碼,我根據程序休眠的時間,讓主線程先退出,然后發現守護線程卻在第二個退出的。因為他不是死循環,所以提前退出了?這么說來,如果一個線程要想是守護線程的話,他必須是死循環?也就是說,從程序表面看,守護線程一定要比用戶線程存活的久一點?這么說來,將一個理論上活的更久的線程,設置成守護線程,僅僅是讓它自動和用戶線程一起消亡罷了。實際上,通過延長上面代碼的daemonPrint()的sleep的時間,也可以驗證守護線程會和用戶線程一起消亡。

下面是程序的日志:

main process begin Tue Oct 16 17:17:29 CST 2018 false thread amount 1
===============main false
main after create thread A Tue Oct 16 17:17:30 CST 2018 false thread amount 2
===============main false
===============Thread-0 true
main after create thread B Tue Oct 16 17:17:33 CST 2018 false thread amount 3
===============main false
===============Thread-0 true
===============Thread-1 false
main Exit: Tue Oct 16 17:17:33 CST 2018 false thread amount 3
===============main false
===============Thread-0 true
===============Thread-1 false
Thread-0 Tue Oct 16 17:17:36 CST 2018 true thread amount 3
===============Thread-0 true // 這一行日志,說明他是守護線程,但是下面卻沒有了他的名字。因為他不是死循環,所以提前退出了?
===============Thread-1 false
===============DestroyJavaVM false
Thread-1 before Counter:1 Tue Oct 16 17:17:36 CST 2018 false thread amount 2
Thread-1 after Counter:2 Tue Oct 16 17:17:39 CST 2018 false thread amount 2
===============Thread-1 false
===============DestroyJavaVM false
Thread-1 before Counter:3 Tue Oct 16 17:17:42 CST 2018 false thread amount 2
Thread-1 after Counter:4 Tue Oct 16 17:17:45 CST 2018 false thread amount 2
===============Thread-1 false
===============DestroyJavaVM false

結論:代碼的邏輯讓守護線程提前於用戶線程消亡的情況下,守護線程並不會主動延長生命和用戶線程一起消亡。但是,代碼的邏輯讓守護線程延遲於用戶線程消亡的情況下,守護線程會提前和用戶線程一起消亡。這樣也可以理解,畢竟CPU資源那么金貴,既然守護線程提前與用戶線程消亡,那他沒有必要賴着占用CPU的資源,對吧?

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM