目錄
1.預設用戶執行緒
2.主動修改為守護線程
2.1 設定線程為守護線程
2.2 設定執行緒池為守護執行緒
3.守護線程 VS 用戶線程
3.1 使用者執行緒
3.2 守護執行緒
3.3 小结
4.守护线程注意事项
4.1 setDaemon 执行顺序
4.2 守护线程的子线程
4.3 join 与守护线程
5.守护线程应用场景
6.守护线程的执行优先级
首頁 Java java教程 Java中使用者線程與守護線程如何使用

Java中使用者線程與守護線程如何使用

May 13, 2023 am 11:28 AM
java

    1.預設用戶執行緒

    Java 語言中無論是執行緒還是執行緒池,預設都是用戶執行緒,因此用戶線程也被變成普通線程。

    以執行緒為例,想要檢視執行緒是否為守護執行緒只需透過呼叫 isDaemon() 方法查詢即可,如果查詢的值為 false 則表示不為守護線程,自然也就屬於使用者線程了,

    如下程式碼所示:

    public static void main(String[] args) throws InterruptedException {
        Thread thread = new Thread(new Runnable() {
            @Override
            public void run() {
                System.out.println("我是子线程");
            }
        });
        System.out.println("子线程==守护线程:" + thread.isDaemon());
        System.out.println("主线程==守护线程:" + Thread.currentThread().isDaemon());
    }
    登入後複製

    以上程式的執行結果為: 

    Java中使用者線程與守護線程如何使用

     從上述結果可以看出,預設主執行緒和建立的新執行緒都為使用者執行緒

    PS:Thread.currentThread() 的意思是取得執行目前程式碼的執行緒實例。

    2.主動修改為守護線程

    守護線程(Daemon Thread)也稱為後台線程或服務線程,守護線程是為使用者線程服務的,當程式中的使用者執行緒全部執行結束之後,守護線程也會跟著結束。

    守護線程的角色就像“服務員”,而用戶線程的角色就像“顧客”,當“顧客”全部走了之後(全部執行結束),那“服務員”(守護線程)也就沒有了存在的意義,所以當一個程式中的全部使用者執行緒都結束執行之後,那麼無論守護執行緒是否還在工作都會隨著使用者執行緒一塊結束,整個程式也會隨之結束運作。

    那要如何將預設的使用者執行緒修改為守護線程呢?

    這個問題要分成兩種情況來回答,首先如果是線程,則可以透過設定 setDaemon(true) 方法將使用者執行緒直接修改為守護線程,而如果是執行緒池則需要透過 ThreadFactory 將執行緒池中的每個執行緒都為守護執行緒才行,接下來我們分別來實作一下。

    2.1 設定線程為守護線程

    如果使用的是線程,可以透過 setDaemon(true) 方法將線程類型變更為守護線程,如下程式碼所示:

     public static void main(String[] args) throws InterruptedException {
         Thread thread = new Thread(new Runnable() {
             @Override
             public void run() {
                 System.out.println("我是子线程");
             }
         });
         // 设置子线程为守护线程
         thread.setDaemon(true);
         System.out.println("子线程==守护线程:" + thread.isDaemon());
         System.out.println("主线程==守护线程:" + Thread.currentThread().isDaemon());
     }
    登入後複製

    以上程式的執行結果為: 

    Java中使用者線程與守護線程如何使用

    2.2 設定執行緒池為守護執行緒

    要把線程池設定為守護線程相對來說很麻煩一些,需要將線程池中的所有線程都設定成守護線程,而這個時候就需要使用 ThreadFactory 來定義執行緒池中每個執行緒的執行緒類型了,具體實作程式碼如下:

    // 创建固定个数的线程池
    ExecutorService threadPool = Executors.newFixedThreadPool(10, new ThreadFactory() {
        @Override
        public Thread newThread(Runnable r) {
            Thread t = new Thread(r);
            // 设置线程为守护线程
            t.setDaemon(false);
            return t;
        }
    });
    登入後複製

    #如下圖所示: 

    Java中使用者線程與守護線程如何使用

    如上圖所示,可以看出,整個程式中有10 個守護線程都是我創建的。其他幾種創建線程池的設定方式類似,都是透過 ThreadFactory 統一設定的,這裡就不一一列舉了。

    3.守護線程 VS 用戶線程

    透過前面的學習我們可以創建兩個不同的線程類型了,那二者有什麼差異呢?接下來我們使用一個小範例來看一下。

    下面我們建立一個線程,分別將這個線程設定為用戶線程和守護線程,並在每個線程中執行一個 for 循環,總共執行10 次資訊列印,每次列印之後休眠100 毫秒,來觀察程式的運作結果。

    3.1 使用者執行緒

    新建的執行緒預設是使用者執行緒,因此我們不需要對執行緒進行任何特殊的處理,執行 for 迴圈即可(總共執行10 次訊息列印,每次列印後休眠100 毫秒),實作程式碼如下:

    /**
     * Author:Java中文社群
     */
    public class DaemonExample {
        public static void main(String[] args) throws InterruptedException {
            Thread thread = new Thread(new Runnable() {
                @Override
                public void run() {
                    for (int i = 1; i <= 10; i++) {
                        // 打印 i 信息
                        System.out.println("i:" + i);
                        try {
                            // 休眠 100 毫秒
                            Thread.sleep(100);
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }
                }
            });
            // 启动线程
            thread.start();
        }
    }
    登入後複製

    以上程式執行結果如下: 

    Java中使用者線程與守護線程如何使用

     從上述結果可以看出,當程式執行完10 次列印後才會正常結束進程。

    3.2 守護執行緒

    /**
     * Author:Java中文社群
     */
    public class DaemonExample {
        public static void main(String[] args) throws InterruptedException {
            Thread thread = new Thread(new Runnable() {
                @Override
                public void run() {
                    for (int i = 1; i <= 10; i++) {
                        // 打印 i 信息
                        System.out.println("i:" + i);
                        try {
                            // 休眠 100 毫秒
                            Thread.sleep(100);
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }
                }
            });
            // 设置为守护线程
            thread.setDaemon(true);
            // 启动线程
            thread.start();
        }
    }
    登入後複製

    #以上程式執行結果如下: 

    Java中使用者線程與守護線程如何使用

     從上述結果可以看出,當線程設定為守護線程之後,整個程式不會等守護線程 for 循環10 次之後再進行關閉,而是當主線程結束之後,守護線程只執行了一次循環就結束運行了,由此可以看出守護線程和使用者線程的不同。

    3.3 小结

    守护线程是为用户线程服务的,当一个程序中的所有用户线程都执行完成之后程序就会结束运行,程序结束运行时不会管守护线程是否正在运行,由此我们可以看出守护线程在 Java 体系中权重是比较低的。

    4.守护线程注意事项

    守护线程的使用需要注意以下三个问题:

    • 守护线程的设置 setDaemon(true) 必须要放在线程的 start() 之前,否则程序会报错。

    • 在守护线程中创建的所有子线程都是守护线程。

    • 使用 jojn() 方法会等待一个线程执行完,无论此线程是用户线程还是守护线程。

    接下来我们分别演示一下,以上的注意事项。

    4.1 setDaemon 执行顺序

    当我们将 setDaemon(true) 设置在 start() 之后,如下代码所示:

    public static void main(String[] args) throws InterruptedException {
        Thread thread = new Thread(new Runnable() {
            @Override
            public void run() {
                for (int i = 1; i <= 10; i++) {
                    // 打印 i 信息
                    System.out.println("i:" + i + ",isDaemon:" +
                                Thread.currentThread().isDaemon());
                    try {
                        // 休眠 100 毫秒
                        Thread.sleep(100);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }
        });
        // 启动线程
        thread.start();
        // 设置为守护线程
        thread.setDaemon(true);
    }
    登入後複製

    以上程序执行结果如下:

    Java中使用者線程與守護線程如何使用

    从上述结果可以看出,当我们将 setDaemon(true) 设置在 start() 之后,不但程序的执行会报错,而且设置的守护线程也不会生效。

    4.2 守护线程的子线程

    public static void main(String[] args) throws InterruptedException {
        Thread thread = new Thread(new Runnable() {
            @Override
            public void run() {
                Thread thread2 = new Thread(new Runnable() {
                    @Override
                    public void run() {
    
                    }
                });
                System.out.println("守护线程的子线程 thread2 isDaemon:" +
                                  thread2.isDaemon());
            }
        });
        // 设置为守护线程
        thread.setDaemon(true);
        // 启动线程
        thread.start();
    
        Thread.sleep(1000);
    }
    登入後複製

    以上程序执行结果如下:

    Java中使用者線程與守護線程如何使用

    从上述结果可以看出,守护线程中创建的子线程,默认情况下也属于守护线程

    4.3 join 与守护线程

    通过 3.2 部分的内容我们可以看出,默认情况下程序结束并不会等待守护线程执行完,而当我们调用线程的等待方法 join() 时,执行的结果就会和 3.2 的结果有所不同,下面我们一起来看吧,

    示例代码如下:

    public static void main(String[] args) throws InterruptedException {
        Thread thread = new Thread(new Runnable() {
            @Override
            public void run() {
                for (int i = 1; i <= 10; i++) {
                    // 打印 i 信息
                    System.out.println("i:" + i);
                    try {
                        // 休眠 100 毫秒
                        Thread.sleep(100);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }
        });
        // 设置为守护线程
        thread.setDaemon(true);
        // 启动线程
        thread.start();
        // 等待线程执行完
        thread.join();
        System.out.println("子线程==守护线程:" + thread.isDaemon());
        System.out.println("主线程==守护线程:" + Thread.currentThread().isDaemon());
    }
    登入後複製

    以上程序执行结果如下:

    Java中使用者線程與守護線程如何使用

    通过上述结果我们可以看出,即使是守护线程,当程序中调用 join() 方法时,程序依然会等待守护线程执行完成之后再结束进程。

    5.守护线程应用场景

    守护线程的典型应用场景就是垃圾回收线程,当然还有一些场景也非常适合使用守护线程,比如服务器端的健康检测功能,对于一个服务器来说健康检测功能属于非核心非主流的服务业务,像这种为了主要业务服务的业务功能就非常合适使用守护线程,当程序中的主要业务都执行完成之后,服务业务也会跟随者一起销毁。

    6.守护线程的执行优先级

    首先来说,线程的类型(用户线程或守护线程)并不影响线程执行的优先级,如下代码所示,定义一个用户线程和守护线程,分别执行 10 万次循环,通过观察最后的打印结果来确认线程类型对程序执行优先级的影响。

    public class DaemonExample {
        private static final int count = 100000;
        public static void main(String[] args) throws InterruptedException {
            // 定义任务
            Runnable runnable = new Runnable() {
                @Override
                public void run() {
                    for (int i = 0; i < count; i++) {
                        System.out.println("执行线程:" + Thread.currentThread().getName());
                    }
                }
            };
            // 创建守护线程 t1
            Thread t1 = new Thread(runnable, "t1");
            // 设置为守护线程
            t1.setDaemon(true);
            // 启动线程
            t1.start();
            // 创建用户线程 t2
            Thread t2 = new Thread(runnable, "t2");
            // 启动线程
            t2.start();
        }
    }
    登入後複製

    以上程序执行结果如下:

    Java中使用者線程與守護線程如何使用

    通过上述结果可以看出,线程的类型不管是守护线程还是用户线程对程序执行的优先级是没有任何影响的,而当我们将 t2 的优先级调整为最大时,整个程序的运行结果就完全不同了,

    如下代码所示:

    public class DaemonExample {
        private static final int count = 100000;
        public static void main(String[] args) throws InterruptedException {
            // 定义任务
            Runnable runnable = new Runnable() {
                @Override
                public void run() {
                    for (int i = 0; i < count; i++) {
                        System.out.println("执行线程:" + Thread.currentThread().getName());
                    }
                }
            };
            // 创建守护线程 t1
            Thread t1 = new Thread(runnable, "t1");
            // 设置为守护线程
            t1.setDaemon(true);
            // 启动线程
            t1.start();
            // 创建用户线程 t2
            Thread t2 = new Thread(runnable, "t2");
            // 设置 t2 的优先级为最高
            t2.setPriority(Thread.MAX_PRIORITY);
            // 启动线程
            t2.start();
        }
    }
    登入後複製

    以上程序执行结果如下: 

    Java中使用者線程與守護線程如何使用

    00000000 通过上述的结果可以看出,程序的类型和程序执行的优先级是没有任何关系,当新创建的线程默认的优先级都是 5 时,无论是守护线程还是用户线程,它们执行的优先级都是相同的,当将二者的优先级设置不同时,执行的结果也会随之改变(优先级设置的越高,最早被执行的概率也越大)。

    以上是Java中使用者線程與守護線程如何使用的詳細內容。更多資訊請關注PHP中文網其他相關文章!

    本網站聲明
    本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn

    熱AI工具

    Undresser.AI Undress

    Undresser.AI Undress

    人工智慧驅動的應用程序,用於創建逼真的裸體照片

    AI Clothes Remover

    AI Clothes Remover

    用於從照片中去除衣服的線上人工智慧工具。

    Undress AI Tool

    Undress AI Tool

    免費脫衣圖片

    Clothoff.io

    Clothoff.io

    AI脫衣器

    Video Face Swap

    Video Face Swap

    使用我們完全免費的人工智慧換臉工具,輕鬆在任何影片中換臉!

    熱工具

    記事本++7.3.1

    記事本++7.3.1

    好用且免費的程式碼編輯器

    SublimeText3漢化版

    SublimeText3漢化版

    中文版,非常好用

    禪工作室 13.0.1

    禪工作室 13.0.1

    強大的PHP整合開發環境

    Dreamweaver CS6

    Dreamweaver CS6

    視覺化網頁開發工具

    SublimeText3 Mac版

    SublimeText3 Mac版

    神級程式碼編輯軟體(SublimeText3)

    熱門話題

    Java教學
    1654
    14
    CakePHP 教程
    1413
    52
    Laravel 教程
    1306
    25
    PHP教程
    1252
    29
    C# 教程
    1225
    24
    突破或從Java 8流返回? 突破或從Java 8流返回? Feb 07, 2025 pm 12:09 PM

    Java 8引入了Stream API,提供了一種強大且表達力豐富的處理數據集合的方式。然而,使用Stream時,一個常見問題是:如何從forEach操作中中斷或返回? 傳統循環允許提前中斷或返回,但Stream的forEach方法並不直接支持這種方式。本文將解釋原因,並探討在Stream處理系統中實現提前終止的替代方法。 延伸閱讀: Java Stream API改進 理解Stream forEach forEach方法是一個終端操作,它對Stream中的每個元素執行一個操作。它的設計意圖是處

    PHP:網絡開發的關鍵語言 PHP:網絡開發的關鍵語言 Apr 13, 2025 am 12:08 AM

    PHP是一種廣泛應用於服務器端的腳本語言,特別適合web開發。 1.PHP可以嵌入HTML,處理HTTP請求和響應,支持多種數據庫。 2.PHP用於生成動態網頁內容,處理表單數據,訪問數據庫等,具有強大的社區支持和開源資源。 3.PHP是解釋型語言,執行過程包括詞法分析、語法分析、編譯和執行。 4.PHP可以與MySQL結合用於用戶註冊系統等高級應用。 5.調試PHP時,可使用error_reporting()和var_dump()等函數。 6.優化PHP代碼可通過緩存機制、優化數據庫查詢和使用內置函數。 7

    PHP與Python:了解差異 PHP與Python:了解差異 Apr 11, 2025 am 12:15 AM

    PHP和Python各有優勢,選擇應基於項目需求。 1.PHP適合web開發,語法簡單,執行效率高。 2.Python適用於數據科學和機器學習,語法簡潔,庫豐富。

    PHP與其他語言:比較 PHP與其他語言:比較 Apr 13, 2025 am 12:19 AM

    PHP適合web開發,特別是在快速開發和處理動態內容方面表現出色,但不擅長數據科學和企業級應用。與Python相比,PHP在web開發中更具優勢,但在數據科學領域不如Python;與Java相比,PHP在企業級應用中表現較差,但在web開發中更靈活;與JavaScript相比,PHP在後端開發中更簡潔,但在前端開發中不如JavaScript。

    PHP與Python:核心功能 PHP與Python:核心功能 Apr 13, 2025 am 12:16 AM

    PHP和Python各有優勢,適合不同場景。 1.PHP適用於web開發,提供內置web服務器和豐富函數庫。 2.Python適合數據科學和機器學習,語法簡潔且有強大標準庫。選擇時應根據項目需求決定。

    Java程序查找膠囊的體積 Java程序查找膠囊的體積 Feb 07, 2025 am 11:37 AM

    膠囊是一種三維幾何圖形,由一個圓柱體和兩端各一個半球體組成。膠囊的體積可以通過將圓柱體的體積和兩端半球體的體積相加來計算。本教程將討論如何使用不同的方法在Java中計算給定膠囊的體積。 膠囊體積公式 膠囊體積的公式如下: 膠囊體積 = 圓柱體體積 兩個半球體體積 其中, r: 半球體的半徑。 h: 圓柱體的高度(不包括半球體)。 例子 1 輸入 半徑 = 5 單位 高度 = 10 單位 輸出 體積 = 1570.8 立方單位 解釋 使用公式計算體積: 體積 = π × r2 × h (4

    PHP的影響:網絡開發及以後 PHP的影響:網絡開發及以後 Apr 18, 2025 am 12:10 AM

    PHPhassignificantlyimpactedwebdevelopmentandextendsbeyondit.1)ItpowersmajorplatformslikeWordPressandexcelsindatabaseinteractions.2)PHP'sadaptabilityallowsittoscaleforlargeapplicationsusingframeworkslikeLaravel.3)Beyondweb,PHPisusedincommand-linescrip

    PHP:許多網站的基礎 PHP:許多網站的基礎 Apr 13, 2025 am 12:07 AM

    PHP成為許多網站首選技術棧的原因包括其易用性、強大社區支持和廣泛應用。 1)易於學習和使用,適合初學者。 2)擁有龐大的開發者社區,資源豐富。 3)廣泛應用於WordPress、Drupal等平台。 4)與Web服務器緊密集成,簡化開發部署。

    See all articles