• 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的线程仅仅了解了部分线程创建和同步相关的小部分知识点,但是其实在编程过程中遇到的事情并不仅仅限于此,所以进行整理,列表如下:

    当你深入了解,你就会发现世界如此广袤,而你对世界的了解则是如此浅薄,请永远保持谦卑的态度。
  • 相关阅读:
    《信息安全专业导论》第十周学习总结
    《信息安全专业导论》第九周学习总结
    《信息安全导论》第八周学习总结
    《信息安全专业导论》第七周学习总结
    《信息安全专业导论》第六周学习总结
    《信息安全专业导论》第五周学习总结
    《信息安全专业导论》第四周学习总结
    《信息安全专业导论》第三周学习总结
    信息安全导论第二周学习总结
    计算机概论速读时的问题
  • 原文地址:https://www.cnblogs.com/liwxmyself/p/15411892.html
Copyright © 2020-2023  润新知