暂无图片
暂无图片
暂无图片
暂无图片
暂无图片

ThreadGroup详解(上)

Alleria Windrunner 2020-05-05
319
我们曾经介绍过,创建线程的时候如果没有显式地指定 ThreadGroup,那么新的线程会被加入与父线程相同的 ThreadGroup 中,在本章中,将详细讲解有关 ThreadGroup 的知识以及各个 API 的使用情况。

ThreadGroup与Thread
在 Java 程序中,默认情况下,新的线程都会被加入到 main 线程所在的 group 中,main 线程的 group 名字同线程名。如同线程存在父子关系一样,ThreadGroup 同样也存在父子关系。下图就很好地说明了父子 thread、父子 threadGroup 以及 thread 和 group 之间的层次关系。

无论如何,线程都会被加入某个 Thread Group 之中。

创建ThreadGroup
创建 ThreadGroup 的语法如下:
    public ThreadGroup(String name)
    public ThreadGroup(ThreadGroup parent,String name)
    创建 ThreadGroup 的语法非常简单,可通过上面某个构造函数来创建,第一个构造函数为 ThreadGroup 赋予了名字,但是该 ThreadGroup 的父 ThreadGroup 是创建它的线程所在的 ThreadGroup;第二个 ThreadGroup 的构造函数赋予 group 名字的同时又显式地指定了父 Group。
      public class ThreadGroupCreator
      {
      public static void main(String[] args)
      {
      //① 获取当前线程的 group
      ThreadGroup currentGroup = Thread.currentThread().getThreadGroup();
      //② 定义一个新的 group
      ThreadGroup group1 = new ThreadGroup("Group1");


      //③ 程序输出 true
      System.out.println(group1.getParent() == currentGroup);


      //④ 定义 group2,指定 group1 为其父 group
      ThreadGroup group2 = new ThreadGroup(group1, "Group2");


      //⑤ 程序输出 true
      System.out.println(group2.getParent() == group1);


      }
      }
      上面的代码定义了一个 group1,没有指定父 group,所以默认父 group 为当前线程所在的 group,在构造 group2 时,显式地指定了其父 group 为 group1。

      复制Thread数组和ThreadGroup数组
      在一个 ThreadGroup 中会加入若干个线程以及子 ThreadGroup,ThreadGroup 为我们提供了若干个方法,可以复制出线程和线程组。

      复制Thread数组
        public int enumerate(Thread[] list)
        public int enumerate(Thread[] list,boolean recurse)
        上述两个方法,会将 ThreadGroup 中的 active 线程全部复制到 Thread 数组中,其中 recurse 参数如果为 true,则该方法会将所有子 group 中的 active 线程都递归到 Thread 数组中,enumerate(Thread[]list)实际上等价于 enumerate(Thread[]true),上面两个方法都调用了 ThreadGroup 的私有方法 enumerate:
          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++) {
          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,并且将该 group 加入到 main group 中,然后定义一个线程 thread 并将其加入到 myGroup 中,最后分别调用 enumerate 的递归和非递归方法,如代如下所示:
            public class ThreadGroupEnumerateThreads
            {
            public static void main(String[] args)
            throws InterruptedException
            {
            //创建一个 ThreadGroup
            ThreadGroup myGroup = new ThreadGroup("MyGroup");
            //创建线程传入 threadgroup
            Thread thread = new Thread(myGroup, () ->
            {
            while (true)
            {
            try
            {
            TimeUnit.SECONDS.sleep(1);
            } catch (InterruptedException e)
            {
            }
            }
            }, "MyThread");
            thread.start();


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




            Thread[] list = new Thread[mainGroup.activeCount()];
            int recurseSize = mainGroup.enumerate(list);
            System.out.println(recurseSize);


            recurseSize = mainGroup.enumerate(list, false);
            System.out.println(recurseSize);
            }
            }
            上面的代码运行之后,最后一个输出会比第一个少1,那是因为代码中将递归 recurse 设置为了 false,myGroup 中的线程将不会包含在内。注意:
            • enumerate 方法获取的线程仅仅是个预估值,并不能百分之百地保证当前 group 的活跃线程,比如在调用复制之后,某个线程结束了生命周期或者新的线程加入了进来,都会导致数据的不准确。

            • enumerate 方法的返回值 int 相较 Thread[] 的长度更为真实,比如定义了数组长度的 Thread 数组,那么 enumerate 方法仅仅会将当前活跃的 thread 分别放进数组中,而返回值 int 则代表真实的数量,并非 Thread 数组的长度,可能是早期版本就有这个方法的缘故(JDK1.0),其实用 List(JDK1.1 版本才引入)会更好一些。


            复制ThreadGroup数组
            来看如下两种方法:
              public int enumerate(ThreadGroup[] list)
              public int enumerate(ThreadGroup[] list,boolean recurse)
              和复制 Thread 数组类似,上述两个方法,主要用于复制当前 ThreadGroup 的子 Group,同样 recurse 会决定是否以递归的方式复制。
                public class ThreadGroupEnumerateThreadGroups
                {
                public static void main(String[] args)
                throws InterruptedException
                {


                ThreadGroup myGroup1 = new ThreadGroup("MyGroup1");
                ThreadGroup myGroup2 = new ThreadGroup(myGroup1,"MyGroup2");




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




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


                int recurseSize = mainGroup.enumerate(list);
                System.out.println(recurseSize);


                recurseSize = mainGroup.enumerate(list, false);
                System.out.println(recurseSize);
                }
                }
                在上面的代码中,myGroup1 的父 group 为 mainGroup,而 myGroup2 的父 group 为 myGroup1,因此上述的代码运行之后,递归复制的结果为2,不递归的情况下为1。
                文章转载自Alleria Windrunner,如果涉嫌侵权,请发送邮件至:contact@modb.pro进行举报,并提供相关证据,一经查实,墨天轮将立刻删除相关内容。

                评论