4、线程组ThreadGroup

4.1 什么是线程组

线程组的作用是:可以批量管理线程或线程组对象,有效地对线程或线程组对象进行组织

或许需要区分一下线程数组、线程池、线程组ThreadGroup。

线程数组就是将线程放入数组中,方便做一些简单的操作(遍历查询、运行、join阻塞)。

线程池的概念是在python线程接触的,由于python的线程提供了通用线程调用方法的方式来替代线程继承创建,可以维持一个一定数量的线程数组以供使用,减少线程频繁创建销毁的开销,原理类似于数据库连接池。这个用法Java好像并不常用。

还有就是线程组ThreadGroup,它所维持的线程结构更像是一个树,提供了一些管理线程组的方法。

JAVA篇: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

  • 创建了线程组树如下

JAVA篇:Java 多线程 (四) 线程组ThreadGroup_数组_02

 

  • 创建了6个子线程,结构如下,并运行所有子线程,所有子线程进入wait状态,使得所有子线程在下面的代码运行时一直处于活动状态

    JAVA篇:Java 多线程 (四) 线程组ThreadGroup_数组_03

     

  • 将线程组1、2、3都设置为守护线程组,线程组4为非守护线程组

  • 调用threadGroup1.enumerate(threads2)成功将线程组1及其子组下全部活动线程复制到threads2数组中

  • 通过interrupt中断线程组1及其子组下全部线程

  • 所有子线程运行完毕销毁后,作为守护线程,线程组3直接销毁,线程组4不是守护线程所以不会自动销毁,线程组1,2因为还有子线程组未销毁,不会自动销毁

    JAVA篇:Java 多线程 (四) 线程组ThreadGroup_守护线程_04

     

  • 手动销毁线程组4,线程组1、2自动销毁

JAVA篇:Java 多线程 (四) 线程组ThreadGroup_线程组_05

 

 

 

 

4.X 参考

Java并发 之 线程组 ThreadGroup 介绍

线程组ThreadGroup分析详解 多线程中篇(三)

Java多线程16:线程组

 

当你深入了解,你就会发现世界如此广袤,而你对世界的了解则是如此浅薄,请永远保持谦卑的态度。