• java 多线程 24 : 线程组


    线程组

    可以把线程归属到某一个线程组中,线程组中可以有线程对象,也可以有线程组,组中还可以有线程,这样的组织结构有点类似于树的形式,如图所示:

     

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

     

    线程关联线程组:1级关联

    所谓1级关联就是父对象中有子对象,但并不创建孙对象。这种情况在开发中很常见,比如创建一些线程时,为了有效对这些线程进行阻止管理,通常情况下是创建一个线程组,然后再将部分线程归属到该组中,以此来对零散的线程对象进行有效的管理。

    看一下简单的1级关联的例子:

    public class MyThread49 implements Runnable
    {
        public void run()
        {
            try
            {
                while (!Thread.currentThread().isInterrupted())
                {
                    System.out.println("ThreadName = " + Thread.currentThread().getName());
                    Thread.sleep(3000);
                }
            }
            catch (InterruptedException e)
            {
                e.printStackTrace();
            }
        }
    }
    public static void main(String[] args)
    {
        MyThread49 mt0 = new MyThread49();
        MyThread49 mt1 = new MyThread49();
        ThreadGroup tg = new ThreadGroup("新建线程组1");
        Thread t0 = new Thread(tg, mt0);
        Thread t1 = new Thread(tg, mt1);
        t0.start();
        t1.start();
        System.out.println("活动的线程数为:" + tg.activeCount());
        System.out.println("线程组的名称为:" + tg.getName());
    }

    看一下运行结果:

    活动的线程数为:2
    ThreadName = Thread-1
    ThreadName = Thread-0
    线程组的名称为:新建线程组1
    ThreadName = Thread-1
    ThreadName = Thread-0
    ThreadName = Thread-1
    ThreadName = Thread-0
    ThreadName = Thread-1
    ThreadName = Thread-0
    ...

    控制台上打印出的信息表示线程组中有两个线程,并且打印出了线程组的名称。另外,两个线程无限隔3秒打印,也符合代码预期

     

    线程关联线程组:多级关联

    所谓的多级关联就是父对象中有子对象,子对象中再创建子对象买也就出现了子孙的效果了。但是这种写法在开发中不太常见,因为线程树如果涉及得复杂反而不利于线程对象的管理,不过JDK确实提供了多级关联的线程树结构。

    多级关联的代码就不写了,简单看一下怎么使用关机关联,查看下JDK API的ThreadGroup构造方法:

    注意一下第二个,假如要使用多级关联一般就是用第二个构造函数。第一个参数表示新线程组的父线程组,第二个参数表示新线程组的名称,有了父线程组和新线程组的名称,自然可以构造出一个新的线程组来了。

    当然用第一个构造方法也是可以的,下一部分就会提到。

    另外注意一点,线程必须启动后才能归到指定线程组中

     

    线程组自动归属特性

    自动归属的意思就是自动归到当前线程组中,看一下例子:

    public static void main(String[] args)
    {
        System.out.println("A处线程:" + 
            Thread.currentThread().getName() + 
            ", 所属线程:" + 
            Thread.currentThread().getThreadGroup().getName() + 
            ", 组中有线程组数量:" + 
            Thread.currentThread().getThreadGroup().activeGroupCount());
        ThreadGroup group = new ThreadGroup("新的组");
        System.out.println("B处线程:" + 
            Thread.currentThread().getName() + 
            ", 所属线程:" + 
            Thread.currentThread().getThreadGroup().getName() + 
            ", 组中有线程组数量:" + 
        Thread.currentThread().getThreadGroup().activeGroupCount());
        ThreadGroup[] tg = new ThreadGroup[Thread.currentThread().getThreadGroup().activeGroupCount()];
        Thread.currentThread().getThreadGroup().enumerate(tg);
        for (int i = 0; i < tg.length; i++)
            System.out.println("第一个线程组名称为:" + tg[i].getName());
    }

    看一下运行结果:

    A处线程:main, 所属线程:main, 组中有线程组数量:0
    B处线程:main, 所属线程:main, 组中有线程组数量:1
    第一个线程组名称为:新的组

    从结果看,实例化了一个group出来,没有指定线程组,那么自动归到当前线程所属的线程组中,也就是隐式地在一个线程组中添加了一个子线程组。

     这里线程组的enumerate是用来拷贝线程组,或者线程数组到指定的数组中的,有两个重载方法,因为线程组没有显示get返回线程组数据或者线程数据给你,只有这个方法可以拷贝


    线程组内加线程组





    以上代码演示了在指定线程组内再加子线程组,可以无限延伸下去





    根线程组

    看一下根线程组: 

    public static void main(String[] args)
    {
        System.out.println(Thread.currentThread().getThreadGroup().getParent().getName());
        System.out.println(Thread.currentThread().getThreadGroup().getParent().getParent().getName());
    }

    看一下运行结果:

    system
    Exception in thread "main" java.lang.NullPointerException
        at com.xrq.example.e49.TestMain49.main(TestMain49.java:11)

    运行结果可以得出两个结论:

    1、根线程组就是系统线程组system

    2、抛空指针异常是因为系统线程组上已经没有线程组了,所以system的getParent()方法返回的是null,对null调用getName()方法自然是NullPointerException

    关于根线程组,看一下ThreadGroup的源码:

    /**
     * Creates an empty Thread group that is not in any Thread group. 
     * This method is used to create the system Thread group.
     */private ThreadGroup() {    // called from C codethis.name = "system";
    this.maxPriority = Thread.MAX_PRIORITY;
    }

    一个私有构造方法,说明不是对开发者开放的。注释上已经写得很清楚了,这是C代码调用的,用于构建系统线程组。

     

    批量停止组内线程

    使用线程组自然是要对线程做批量管理的,到目前为止我们似乎都没有看见如何对线程组内的线程做批量操作,最后来看一下批量操作线程组内的线程:

    public class MyThread50 extends Thread
    {
        public MyThread50(ThreadGroup tg, String name)
        {
            super(tg, name);
        }
        
        public void run()
        {
            System.out.println("ThreadName = " + Thread.currentThread().getName() + 
                    "准备开始死循环了");
            while (!this.isInterrupted()){}
            System.out.println("ThreadName = " + Thread.currentThread().getName() + 
                    "结束了");
        }
    }

    开3个线程:

    public static void main(String[] args) throws InterruptedException
    {
        ThreadGroup tg = new ThreadGroup("我的线程组");
        MyThread50 mt = null;
        for (int i = 0; i < 3; i++)
        {
            mt = new MyThread50(tg, "线程" + i);
            mt.start();
        }
        Thread.sleep(5000);
        tg.interrupt();
        System.out.println("调用了interrupt()方法");
    }

    看一下运行结果:

    ThreadName = 线程0准备开始死循环了
    ThreadName = 线程2准备开始死循环了
    ThreadName = 线程1准备开始死循环了
    调用了interrupt()方法
    ThreadName = 线程2结束了
    ThreadName = 线程1结束了
    ThreadName = 线程0结束了

    看到调用了ThreadGroup中的interrupt()方法批量中断了线程组内的线程,这就是ThreadGroup的作用。更多线程组的操作可以查看JDK API。



    递归与非递归获取组内对象


    看到在调用emumerate方法的时候可以传入第二个参数就是是否递归下去



  • 相关阅读:
    JVM命令
    悲观锁与乐观锁
    bean生命周期
    MyBatis面试题
    微服务面试题
    MyCat简介
    十大排序算法
    h5跳转微信公众号关注页面
    Maven项目 配置文件放在resources标记的目录下 却没有打包进target文件
    本机号码一键登录原理与应用
  • 原文地址:https://www.cnblogs.com/signheart/p/00f1fc72786a89656ae2683ab56fdd94.html
Copyright © 2020-2023  润新知