Java多线程拾遗(六)——ThreadGroup初探

前言

关于ThreadGroup的话,其实使用的并不多,在没有线程池的时代,还是用的很多,但是有些老的源码阅读起来还是会有线程组的影子,这里与《Java高并发详解 》一书保持一致,进行一个简单的总结

线程组与线程之间的关系其实可以类比数据结构中的树形结构。《Java高并发详解 》一书中有这样一张图,ThreadGroup可以看成树的非叶子节点,Thread可以看成是树的叶子节点。

Java多线程拾遗(六)——ThreadGroup初探_第1张图片

线程组的创建

通过查看ThreadGroup的源码发现,其实ThreadGroup对外提供的公共的构造方法只有如下两个

public ThreadGroup(String name);//只指定线程组的名称,父线程组默认为当前线程组
public ThreadGroup(ThreadGroup parent, String name);//指定父线程组,同时指定线程组的名称

直接看如下实例吧:

/**
 * autor:liman
 * createtime:2020/6/21
 * comment:线程组的创建
 */
@Slf4j
public class ThreadGroupCreate {

    public static void main(String[] args) {

        log.info("main线程组的名称为:{}",Thread.currentThread().getThreadGroup().getName());

        //父线程默认为main 线程组
        ThreadGroup threadGroupOne = new ThreadGroup("thread group one");
        Thread threadOne = new Thread(threadGroupOne,()->{
            try {
                Thread currentThread = Thread.currentThread();
                ThreadGroup currentGroup = currentThread.getThreadGroup();
                ThreadGroup parentGroup = currentThread.getThreadGroup().getParent();
                //当前线程组的活动线程个数
                int currentActiveAcount = currentGroup.activeCount();
				//父线程组的活动线程个数(api文档中介绍,无法访问父线程的相关信息,其实是可以访问的)
                int parentActiveAcount = parentGroup.activeCount();
                log.info("当前线程:{},所属线程组:{},父线程组:{}",currentThread.getName(),currentGroup.getName(),parentGroup.getName());
                log.info("当前线程组的活动线程数量:{},父线程组的活动线程数量:{}",currentActiveAcount,parentActiveAcount);
                Thread.sleep(10_000L);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        },"threadOne");//创建线程的时候,显示指定线程组
        threadOne.start();

        ThreadGroup threadGroupTwo = new ThreadGroup(threadGroupOne,"thread group two");
        Thread threadTwo = new Thread(threadGroupTwo,()->{
            try {
                Thread currentThread = Thread.currentThread();
                ThreadGroup currentGroup = currentThread.getThreadGroup();
                ThreadGroup parentGroup = currentThread.getThreadGroup().getParent();
                //当前线程组的活动线程个数
                int currentActiveAcount = currentGroup.activeCount();
				//父线程组的活动线程个数(api文档中介绍,无法访问父线程的相关信息,其实是可以访问的)
                int parentActiveAcount = parentGroup.activeCount();
                log.info("当前线程:{},所属线程组:{},父线程组:{}",currentThread.getName(),currentGroup.getName(),parentGroup.getName());
                log.info("当前线程组的活动线程数量:{},父线程组的活动线程数量:{}",currentActiveAcount,parentActiveAcount);
                Thread.sleep(10_000L);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        },"threadTwo");
        threadTwo.start();
    }
}

创建了两个线程组,第一个没有显示指定父线程组,则会将这个线程组的父线程组设置为启动该线程组的主线程组。第二个显示设置了线程组。具体运行结果如下所示:

在这里插入图片描述

线程组的复制

上述代码线程组与线程之间的关系如下所示

Java多线程拾遗(六)——ThreadGroup初探_第2张图片

获取活跃线程数量

其实就是一个activeCount方法,JDK文档中,关于这个方法的说明如下:

Returns an estimate of the number of active threads in this thread,group and its subgroups. Recursively iterates over all subgroups in,this thread group.

只是返回一个预估的活跃线程数量,会递归的找出子线程中的活跃线程数量

对上述代码中的Thread Group One和Thread Group Two调用activeCount会得到如下结果

在这里插入图片描述

复制

复制线程

复制线程只是单纯的复制线程组中的线程,有递归和非递归两种

具体源码如下

/**
默认就是递归拷贝的方式
*/
public int enumerate(Thread list[]) {
    checkAccess();
    return enumerate(list, 0, true);
}

public int enumerate(Thread list[], boolean recurse) {
    checkAccess();
    return enumerate(list, 0, recurse);
}

具体实例

/**
 * autor:liman
 * createtime:2020/6/21
 * comment:线程组的复制
 */
@Slf4j
public class ThreadGroupDulip {

    public static void main(String[] args) {
        log.info("main线程组的名称为:{}",Thread.currentThread().getThreadGroup().getName());

        //父线程默认为main 线程组
        ThreadGroup threadGroupOne = new ThreadGroup("thread group one");
        Thread threadOne = new Thread(threadGroupOne,()->{
            try {
                Thread currentThread = Thread.currentThread();
                ThreadGroup currentGroup = currentThread.getThreadGroup();
                ThreadGroup parentGroup = currentThread.getThreadGroup().getParent();
                Thread.sleep(10_000L);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        },"threadOne");//创建线程的时候,显示指定线程组
        threadOne.start();

        ThreadGroup threadGroupTwo = new ThreadGroup(threadGroupOne,"thread group two");
        Thread threadTwo = new Thread(threadGroupTwo,()->{
            try {
                Thread currentThread = Thread.currentThread();
                ThreadGroup currentGroup = currentThread.getThreadGroup();
                ThreadGroup parentGroup = currentThread.getThreadGroup().getParent();
                Thread.sleep(10_000L);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        },"threadTwo");
        threadTwo.start();

        Thread[] targetThreadList = new Thread[threadGroupOne.activeCount()];

        duplicate(threadGroupOne,targetThreadList);
    }

    public static void duplicate(ThreadGroup originalThreadGroup,Thread[] threadList){
        String orginalThreadGroupName = originalThreadGroup.getName();
        originalThreadGroup.enumerate(threadList,false);//无递归拷贝
        log.info("线程组:{},无递归拷贝后的线程名称为",orginalThreadGroupName);
        Arrays.asList(threadList).forEach(t->{if(t!=null) log.info("{}",t.getName());});

        //这里也可以不指定第二个参数为true,因为默认为true,即默认递归获取拷贝线程
        originalThreadGroup.enumerate(threadList,true);
        log.info("线程组:{},递归拷贝后的线程名称为",orginalThreadGroupName);
        Arrays.asList(threadList).forEach(t->{if(t!=null) log.info("{}",t.getName());});
    }
}

在这里插入图片描述

复制线程组

与复制线程大同小异,源码的复制拷贝方法名称与复制线程一样

/**
默认就是递归拷贝的方式
*/
public int enumerate(ThreadGroup list[]) {
    checkAccess();//评估当前线程是否有权限改变这个线程组,如果没有权限,则会抛出异常。
    return enumerate(list, 0, true);
}

public int enumerate(ThreadGroup list[], boolean recurse) {
    checkAccess();
    return enumerate(list, 0, recurse);
}

线程组的中断和销毁

中断

线程组的interrupt会将线程组中所有的线程都设置中断标志。这个直接看源码

public final void interrupt() {
    int ngroupsSnapshot;
    ThreadGroup[] groupsSnapshot;
    synchronized (this) {
        checkAccess();
        //遍历每一个线程,然后将其中的线程中断
        for (int i = 0 ; i < nthreads ; i++) {
            threads[i].interrupt();
        }
        ngroupsSnapshot = ngroups;
        if (groups != null) {
            groupsSnapshot = Arrays.copyOf(groups, ngroupsSnapshot);
        } else {
            groupsSnapshot = null;
        }
    }
    for (int i = 0 ; i < ngroupsSnapshot ; i++) {
        groupsSnapshot[i].interrupt();
    }
}

销毁

对于线程组而言,并不是真正意义上的销毁,而是简单将该线程组从父线程中移出。但是前提是,被删除的线程组中没有活跃线程存在。

守护线程组

与线程一样,线程组也有一个守护线程组的概念,对于线程组而言,如果这个线程组是守护线程,当其中没有任何一个活跃线程的时候线程组将会自动销毁

总结

对于线程组而言,没有过多可总结的,只是有些框架的源码还用到线程组这个东西,需要一个简单的总结

你可能感兴趣的:(多线程)