JAVA篇:Java 多線程 (四) 線程組ThreadGroup


4、線程組ThreadGroup

4.1 什么是線程組

線程組的作用是:可以批量管理線程或線程組對象,有效地對線程或線程組對象進行組織

或許需要區分一下線程數組、線程池、線程組ThreadGroup。

線程數組就是將線程放入數組中,方便做一些簡單的操作(遍歷查詢、運行、join阻塞)。

線程池的概念是在python線程接觸的,由於python的線程提供了通用線程調用方法的方式來替代線程繼承創建,可以維持一個一定數量的線程數組以供使用,減少線程頻繁創建銷毀的開銷,原理類似於數據庫連接池。這個用法Java好像並不常用。

還有就是線程組ThreadGroup,它所維持的線程結構更像是一個樹,提供了一些管理線程組的方法。

 

4.2 ThreadGroup的構造方法

ThreadGroup和Thread一樣定義在java.lang下面,提供了兩個構造函數,其中一個構造函數可指定父線程組。

//構造方法1:構造新線程組,新線程組的默認父項是正在運行線程所在的線程組
ThreadGroup(String name)
//構造方法2:構造新線程組,並將指定線程組作為父線程組
ThreadGroup(ThreadGroup parent, String name)
    public static void test1(){
        ThreadGroup threadGroup1 = new ThreadGroup("線程組1");
        System.out.println(threadGroup1.getName());//線程組1
        System.out.println(threadGroup1.getParent().getName());//main
    }

 

ThreadGroup提供了方法checkAccess(),用來確定當前運行的線程是否有權限修改此線程組。當用戶在使用構建方法在默認或指定線程組下構建新的線程組,會調用該方法,檢查當前線程是否有權限修改父線程組,若沒有權限,會拋出錯誤“SecurityException”。

4.3 ThreadGroup提供的一些方法

ThreadGroup的方法與Thread向對應,只是提供了一些統一的管理操作的方法。

  1. String getName():返回此線程組的名稱。

  2. ThreadGroup getParent():返回此線程組的父級。

  3. boolean parentOf(ThreadGroup g):測試此線程組是否是其祖先線程組之一。

  4. void interrupt():中斷此線程組中的所有線程。

  5. void setMaxPriority(int pri):設置組的最大優先級。

  6. int getMaxPriority():返回此線程組的最大優先級。

  7. void setDaemon(boolean daemon):更改此線程組的守護程序狀態。守護線程組最后一個線程停止或最后一個線程組被銷毀時自動銷毀。

  8. boolean isDaemon():測試此線程組是否是守護線程組。

  9. void destroy():銷毀此線程組及其所有子組, 此線程組必須為空,表示此線程組中的所有線程已停止。如果線程組不為空或線程組已被破壞,則拋出"IllegalThreadStateException"。

  10. boolean isDestroyed():測試此線程組是否已被破壞。

  11. int activeCount():返回此線程組及其子組中活動線程數的估計。

  12. int activeGroupCount():返回此線程組及其子組中活動組數的估計。

  13. void checkAccess():確定當前運行的線程是否有權限修改此線程組。在ThreadGroup中涉及任意線程、線程組的操作都需要對這些線程線程組的權限進行檢查,若無權限都會拋出“SecurityException”

  14. int enumerate(Thread[] list):將此線程組及其子組中的每個活動線程復制到指定的數組中。相當於enumerate(Thread[] list, true)

  15. int enumerate(Thread[] list, boolean recurse):若recurse為true,遞歸將此線程組中的每個活動線程復制到指定的數組中。

  16. int enumerate(ThreadGroup[] list):復制到該線程組及其子組中每個活動子組的指定數組引用。

  17. int enumerate(ThreadGroup[] list, boolean recurse):復制到該線程組中每個活動子組的指定數組引用。

  18. void list():將有關此線程組的信息打印到標准輸出。

  19. String toString():返回此Thread組的字符串表示形式。

  20. void uncaughtException(Thread t, Throwable e):當此線程組中的線程因為一個未捕獲的異常由Java Virtual Machine調用,而線程不具有特定Thread.UncaughtExceptionHandler安裝。

  21. void resume():已棄用,這種方法僅與Thread.suspend和ThreadGroup.suspend一起使用 ,這兩種方法都已經被棄用,因為它們本身就是死鎖的。 詳見Thread.suspend() 。

  22. void stop():已棄用,這種方法本質上是不安全的。 詳見Thread.stop() 。

  23. void suspend():已棄用,這種方法本質上是死鎖的。 詳見Thread.suspend() 。

  24. boolean allowThreadSuspension(boolean b):已棄用,此呼叫的定義取決於suspend() ,它已被棄用。 此外,從未指定此調用的行為。

4.4 測試代碼

代碼

    public  void test1(){
        System.out.println(String.format("主線程:獲取主線程的父線程組名字%s",Thread.currentThread().getThreadGroup().getParent().getName()));
        /* 創建線程組樹 */
        System.out.println("*****創建線程組樹1作為2、3父項,2作為4父項");
        ThreadGroup threadGroup1 = new ThreadGroup("線程組1");
        ThreadGroup threadGroup2 = new ThreadGroup(threadGroup1,"線程組2");
        ThreadGroup threadGroup3 = new ThreadGroup(threadGroup1,"線程組3");
        ThreadGroup threadGroup4 = new ThreadGroup(threadGroup2,"線程組4");
        System.out.println(String.format("主線程:線程組1是否是線程組2的父線程組%b",threadGroup1.parentOf(threadGroup2)));
        System.out.println(String.format("主線程:線程組1是否是線程組4的父線程組%b",threadGroup1.parentOf(threadGroup4)));
        System.out.println(String.format("主線程:線程組3是否是線程組4的父線程組%b",threadGroup3.parentOf(threadGroup4)));
​
        /* 創建線程組中的子線程,線程組1:1,線程組2:1,線程組3:1,線程組4:3 */
        System.out.println("*****創建線程組中的子線程,線程組1:1,線程組2:1,線程組3:1,線程組4:3,並運行");
        Thread[] threads = new Thread[6];
       threads[0]= new myThread(threadGroup1,"線程組1-線程1");
       threads[1]= new myThread(threadGroup2,"線程組2-線程2");
       threads[2]= new myThread(threadGroup3,"線程組3-線程3");
       threads[3]= new myThread(threadGroup4,"線程組4-線程41");
       threads[4]= new myThread(threadGroup4,"線程組4-線程42");
       threads[5]= new myThread(threadGroup4,"線程組4-線程43");
​
        for(int i=0;i<6;i++){
            threads[i].start();
        }
​
        /* 將線程組1,2,3都設置為守護線程組 */
        System.out.println("*****將線程組1,2,3都設置為守護線程組");
        threadGroup1.setDaemon(true);
        threadGroup2.setDaemon(true);
        threadGroup3.setDaemon(true);
​
        System.out.println(String.format("主線程:線程組1是否是守護線程%b",threadGroup1.isDaemon()));
        System.out.println(String.format("主線程:線程組3是否是守護線程%b",threadGroup3.isDaemon()));
        System.out.println(String.format("主線程:線程組4是否是守護線程%b",threadGroup4.isDaemon()));
​
        try {
            Thread.sleep(10);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
​
        /* 復制線程組先全部子線程到數組*/
        System.out.println("*****復制線程組先全部活動子線程到數組並遍歷");
        Thread[] threads2 = new Thread[threadGroup1.activeCount()];
        int tc = threadGroup1.enumerate(threads2);
        System.out.println(String.format("主線程:線程組1中%d個子線程成功復制到線程數組threads",tc));
        for(int i=0;i<tc;i++){
            System.out.println(threads2[i]);
        }
​
​
​
        /*  中斷全部等待的子線程 */
        System.out.println("*****中斷全部等待的子線程");
        threadGroup1.interrupt();
​
        for(int i=0;i<tc;i++){
            try {
                threads2[i].join();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        System.out.println("*****所有子線程運行完畢並銷毀");
        System.out.println(String.format("主線程:線程組1下運行線程估計%d",threadGroup1.activeCount()));
        System.out.println(String.format("主線程:線程組1下運行線程組估計%d",threadGroup1.activeGroupCount()));
​
        System.out.println("*****線程組1,2,3作為守護線程,線程組3會自動銷毀,1和2會因為4無法銷毀");
​
        System.out.println(String.format("主線程:線程組1是否被銷毀%b",threadGroup1.isDestroyed()));
        System.out.println(String.format("主線程:線程組3是否被銷毀%b",threadGroup3.isDestroyed()));
        System.out.println(String.format("主線程:線程組4是否被銷毀%b",threadGroup4.isDestroyed()));
​
        System.out.println("*****手動銷毀線程組4");
        threadGroup4.destroy();
        System.out.println(String.format("主線程:線程組1是否被銷毀%b",threadGroup1.isDestroyed()));
​
​
​
    }
​
    class myThread extends Thread{
        public myThread(ThreadGroup tg,String name){
            super(tg,name);
        }
        @Override
        public void run(){
            synchronized ("A"){
                System.out.println(String.format("子線程 %s:調用wait()進入休眠",getName()));
                try {
                    "A".wait();
                } catch (InterruptedException e) {
                    System.out.println(String.format("子線程 %s:被中斷!",getName()));
                }
            }
​
        }
​
    }

 

運行結果

主線程:獲取主線程的父線程組名字system
*****創建線程組樹1作為2、3父項,2作為4父項
主線程:線程組1是否是線程組2的父線程組true
主線程:線程組1是否是線程組4的父線程組true
主線程:線程組3是否是線程組4的父線程組false
*****創建線程組中的子線程,線程組1:1,線程組2:1,線程組3:1,線程組4:3,並運行
*****將線程組1,2,3都設置為守護線程組
主線程:線程組1是否是守護線程true
子線程 線程組1-線程1:調用wait()進入休眠
主線程:線程組3是否是守護線程true
子線程 線程組4-線程42:調用wait()進入休眠
主線程:線程組4是否是守護線程false
子線程 線程組3-線程3:調用wait()進入休眠
子線程 線程組4-線程41:調用wait()進入休眠
子線程 線程組2-線程2:調用wait()進入休眠
子線程 線程組4-線程43:調用wait()進入休眠
*****復制線程組先全部活動子線程到數組並遍歷
主線程:線程組1中6個子線程成功復制到線程數組threads
Thread[線程組1-線程1,5,線程組1]
Thread[線程組2-線程2,5,線程組2]
Thread[線程組4-線程41,5,線程組4]
Thread[線程組4-線程42,5,線程組4]
Thread[線程組4-線程43,5,線程組4]
Thread[線程組3-線程3,5,線程組3]
*****中斷全部等待的子線程
子線程 線程組1-線程1:被中斷!
子線程 線程組3-線程3:被中斷!
子線程 線程組4-線程41:被中斷!
子線程 線程組4-線程42:被中斷!
子線程 線程組4-線程43:被中斷!
子線程 線程組2-線程2:被中斷!
*****所有子線程運行完畢並銷毀
主線程:線程組1下運行線程估計0
主線程:線程組1下運行線程組估計2
*****線程組1,2,3作為守護線程,線程組3會自動銷毀,1和2會因為4無法銷毀
主線程:線程組1是否被銷毀false
主線程:線程組3是否被銷毀true
主線程:線程組4是否被銷毀false
*****手動銷毀線程組4
主線程:線程組1是否被銷毀true

 

解析

  • 主線程main所屬線程組的父線程組是system

  • 創建了線程組樹如下

 

  • 創建了6個子線程,結構如下,並運行所有子線程,所有子線程進入wait狀態,使得所有子線程在下面的代碼運行時一直處於活動狀態

     

  • 將線程組1、2、3都設置為守護線程組,線程組4為非守護線程組

  • 調用threadGroup1.enumerate(threads2)成功將線程組1及其子組下全部活動線程復制到threads2數組中

  • 通過interrupt中斷線程組1及其子組下全部線程

  • 所有子線程運行完畢銷毀后,作為守護線程,線程組3直接銷毀,線程組4不是守護線程所以不會自動銷毀,線程組1,2因為還有子線程組未銷毀,不會自動銷毀

     

  • 手動銷毀線程組4,線程組1、2自動銷毀

 

 

 

 

4.X 參考

Java並發 之 線程組 ThreadGroup 介紹

線程組ThreadGroup分析詳解 多線程中篇(三)

Java多線程16:線程組

 

0、JAVA多線程編程

Java多線程編程所涉及的知識點包含線程創建、線程同步、線程間通信、線程死鎖、線程控制(掛起、停止和恢復)。之前 JAVA篇:Java的線程僅僅了解了部分線程創建和同步相關的小部分知識點,但是其實在編程過程中遇到的事情並不僅僅限於此,所以進行整理,列表如下:

 


免責聲明!

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



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