Java高并发编程详解系列-六

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/nihui123/article/details/89858985

在之前的分享中提到过关于Thread的部分源码分析,其中提到的一个比较关键的概念就是关于ThreadGroup的概念,也就是说每个一个线程创建之后都会属于一个线程组。每个线程组也可以有自己的父线程组,也可以有自己的子线程组。就想每个线程既有自己的子线程也有自己的父线程。这次分享就是主要来探讨一下关于线程组TreadGroup

ThreadGroup

  在之前的时候我们分析过,对于任意一个创建的新的线程都会被加入到main线程所在的线程组中,而对于main线程来说,是有一个与线程同样名字的线程组。也就是说线程和线程组有包含的关系,而线程组之间有父子关系,线程与线程之间也有父子关系。

创建ThreadGroup

  首先看一下关于ThreadGroup的源码,提供了一个无参构造函数和三个有参构造函数
在这里插入图片描述
无参构造
设置了三个属性,线程组名称、最大优先级以及父线程组

  private ThreadGroup() {     // called from C code
        this.name = "system";
        this.maxPriority = Thread.MAX_PRIORITY;
        this.parent = null;
    }

有参构造

private ThreadGroup(Void unused, ThreadGroup parent, String name) {
     this.name = name;
     this.maxPriority = parent.maxPriority;
     this.daemon = parent.daemon;
     this.vmAllowSuspension = parent.vmAllowSuspension;
     this.parent = parent;
     parent.add(this);
}

  创建ThreadGroup的方法很简单,就是调用几个构造函数。通过上面的代码分析来看,创建ThreadGroup的时候必须为ThreadGroup添加一个name属性值。或者指定一个父线程组,如果没有指定的话默认为主线程所在线程组。

public class ThreadGroupTest {

    public static void main(String[] args) {
        //获取当前线程的线程组
        ThreadGroup main = Thread.currentThread().getThreadGroup();
        ThreadGroup group = new ThreadGroup("ThreadGroup-1");
        System.out.println(group.getParent()==main);
        ThreadGroup group1 = new ThreadGroup(group,"ThreadGroup-2");
        System.out.println(group1.getParent()==group);
    }
}

从上面代码中可以看到首先获取到了当前线程所在的线程组,然后创建了一个ThreadGroup-1的线程组,并没有指定对应的线程组,然后又对创建的线程与当前线程的线程组进行判断结果为True,也就是说明如果不指定线程组默认是以当前线程的线程组作为新建线程组的父线程组。如果指定对应的线程组则为指定的线程组。

Thread数组与ThreadGroup数组的基本操作

Thread数组
  查看ThreadGroup的源码,我们会发现有如下的方法,这个方法的主要的作用是将ThreadGroup中的所有处于active的线程都添加到一个Thread数组中,其中第二个参数表示是否采用递归与非递归的方式。

private int enumerate(Thread list[], int n, boolean recurse) {
        int ngroupsSnapshot = 0;
        ThreadGroup[] groupsSnapshot = null;
        synchronized (this) {
            if (destroyed) {
                return 0;
            }
            int nt = nthreads;
            if (nt > list.length - n) {
                nt = list.length - n;
            }
            for (int i = 0; i < nt; i++) {
			  //判断是或否处于Active状态
                if (threads[i].isAlive()) {
                    list[n++] = threads[i];
                }
            }
            if (recurse) {
                ngroupsSnapshot = ngroups;
                if (groups != null) {
                    groupsSnapshot = Arrays.copyOf(groups, ngroupsSnapshot);
                } else {
                    groupsSnapshot = null;
                }
            }
        }
        if (recurse) {
            for (int i = 0 ; i < ngroupsSnapshot ; i++) {
                n = groupsSnapshot[i].enumerate(list, n, true);
            }
        }
        return n;
    }

测试代码,首先创建一个测试的ThreadGroup,将这个线程组加入到主线程组中,将创建的线程加入到测试TreadGroup中,调用递归和非递归的方法。

public class TestThreadEnumerate {
    public static void main(String[] args) throws InterruptedException {
        //创建一个ThreadGroup
        ThreadGroup testGroup = new ThreadGroup("TestGroup");
        Thread thread = new Thread(testGroup,()->{
            while (true){
                try {
                    TimeUnit.SECONDS.sleep(1);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        },"TestThread");
        thread.start();

        TimeUnit.MILLISECONDS.sleep(2);
        ThreadGroup mainGroup = Thread.currentThread().getThreadGroup();
        Thread[] threads = new Thread[mainGroup.activeCount()];
        int recurseSize = mainGroup.enumerate(threads);
        System.out.println(recurseSize);

        recurseSize = mainGroup.enumerate(threads,false);
        System.out.println(recurseSize);
    }
}

上面代码的执行结果如下,会发现第二个输出要比第一个输出少1个,这个是因为在递归过程中在testGroup中的线程将不会被包含
在这里插入图片描述
注意

  1. 通过上面的enumerate方法获取到的只是一个估计值,也就是说不能保证当前的线程组总的线程都是active的。
  2. 但是通过enumerate方法返回的int值要比Thread数组的长度更有效,因为这个返回值表示的是active的线程数,并不是整个数组的长度。

ThreadGroup数组
  与上面Thread一样也是有一个enumerate方法只不过这个方法的参数传入的是一个ThreadGroup的数组。

private int enumerate(ThreadGroup list[], int n, boolean recurse) {
        int ngroupsSnapshot = 0;
        ThreadGroup[] groupsSnapshot = null;
        synchronized (this) {
            if (destroyed) {
                return 0;
            }
            int ng = ngroups;
            if (ng > list.length - n) {
                ng = list.length - n;
            }
            if (ng > 0) {
                System.arraycopy(groups, 0, list, n, ng);
                n += ng;
            }
            if (recurse) {
                ngroupsSnapshot = ngroups;
                if (groups != null) {
                    groupsSnapshot = Arrays.copyOf(groups, ngroupsSnapshot);
                } else {
                    groupsSnapshot = null;
                }
            }
        }
        if (recurse) {
            for (int i = 0 ; i < ngroupsSnapshot ; i++) {
                n = groupsSnapshot[i].enumerate(list, n, true);
            }
        }
        return n;
    }

可以使用同样的思路编写代码测试这个方法对于ThreadGroup的使用

public class TestThreadGroupEnumerate {
    public static void main(String[] args) throws InterruptedException {
        ThreadGroup group1 = new ThreadGroup("TestGroup1");
        ThreadGroup group2 = new ThreadGroup(group1,"TestGroup1");

        TimeUnit.MILLISECONDS.sleep(2);
        ThreadGroup mainGroup = Thread.currentThread().getThreadGroup();

        ThreadGroup[] list = new ThreadGroup[mainGroup.activeGroupCount()];

        int recureseSize = mainGroup.enumerate(list);
        System.out.println(recureseSize);
        recureseSize = mainGroup.enumerate(list,false);
        System.out.println(recureseSize);
    }
}

ThreadGroup基本操作

  对于ThreadGroup源码的查看会发现它并不是只对线程进行管理,更主要的功能是对线程进行有计划的组织。

int activeCount()
返回此线程组中活动线程的估计数。
int activeGroupCount()
返回此线程组中活动线程组的估计数。
int getMaxPriority()
返回此线程组的最高优先级。
String getName()
返回此线程组的名称。
ThreadGroup getParent()
返回此线程组的父线程组。
void interrupt()
中断此线程组中的所有线程。
boolean isDaemon()
测试此线程组是否为一个后台程序线程组
boolean isDestroyed()
测试此线程组是否已经被销毁。
void list()
将有关此线程组的信息打印到标准输出。
boolean parentOf(ThreadGroup g)
测试此线程组是否为线程组参数或其祖先线程组之一。

public class TestThreadOtherMethod {

    public static void main(String[] args) throws InterruptedException {
        ThreadGroup group = new ThreadGroup("group1");

        Thread thread = new Thread(group,()->{
            while (true){
                try {
                    TimeUnit.SECONDS.sleep(1);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        },"thread");
        thread.setDaemon(true);
        thread.start();

        TimeUnit.MILLISECONDS.sleep(1);

        ThreadGroup mainGroup = Thread.currentThread().getThreadGroup();

        System.out.println("activeCount="+mainGroup.activeCount());
        System.out.println("activeGroupCount="+mainGroup.activeGroupCount());
        System.out.println("getMaxPriority="+mainGroup.getMaxPriority());
        System.out.println("getName="+mainGroup.getName());
        System.out.println("getParent="+mainGroup.getParent());
        mainGroup.list();
        System.out.println("parentOf="+mainGroup.parentOf(group));
        System.out.println("parentof="+mainGroup.parentOf(mainGroup));
    }
}

在这里插入图片描述
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();
        }
    }

  分析源码会看到在这个方法内部调用的是thread的interrupt方法,并且会递归获取子线程组。中断所有的线程。

destroy()
  这个方法用于销毁ThreadGroup线程组,这个方法只针对没有任何active线程的线程组。

public final void destroy() {
        int ngroupsSnapshot;
        ThreadGroup[] groupsSnapshot;
        synchronized (this) {
            checkAccess();
            if (destroyed || (nthreads > 0)) {
                throw new IllegalThreadStateException();
            }
            ngroupsSnapshot = ngroups;
            if (groups != null) {
                groupsSnapshot = Arrays.copyOf(groups, ngroupsSnapshot);
            } else {
                groupsSnapshot = null;
            }
            if (parent != null) {
                destroyed = true;
                ngroups = 0;
                groups = null;
                nthreads = 0;
                threads = null;
            }
        }
        for (int i = 0 ; i < ngroupsSnapshot ; i += 1) {
            groupsSnapshot[i].destroy();
        }
        if (parent != null) {
            parent.remove(this);
        }
    }

实例代码

public class ThreadGroupDestroy {
    public static void main(String[] args) {
        ThreadGroup group = new ThreadGroup("TestGroup");

        ThreadGroup mainGroup = Thread.currentThread().getThreadGroup();

        System.out.println("group.isDestroyed="+group.isDestroyed());
        mainGroup.list();

        group.isDestroyed();

        System.out.println("group.isDestoryed="+group.isDestroyed());
        mainGroup.list();
    }
}

在这里插入图片描述
setDaemon()
  将线程组设置为守护线程组,这个属性被设置为true的时候,当线程组中没有active线程的时候线程组会自动destroy。

public class ThreadGroupDaemon {
    public static void main(String[] args) throws InterruptedException {
        ThreadGroup group1 = new ThreadGroup("TestGroup1");
        new Thread(group1,()->{
            try {
                TimeUnit.SECONDS.sleep(1);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        },"test_thread1").start();

        ThreadGroup group2 = new ThreadGroup("TestGroup2");
        new Thread(group2,()->{
            try {
                TimeUnit.SECONDS.sleep(1);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        },"test_thread2").start();

        group2.setDaemon(true);
        TimeUnit.SECONDS.sleep(3);
        System.out.println(group1.isDestroyed());
        System.out.println(group2.isDestroyed());
    }
}

总结

  在这篇文章中主要介绍了ThreadGroup与Thread的关系,对ThreadGroup的API做了简单的分析以及简单的介绍。

猜你喜欢

转载自blog.csdn.net/nihui123/article/details/89858985