概述 线程组表示一组线程。 此外线程组还可以包括其他线程组。 线程组形成一个树,其中除初始线程组(系统线程组)之外的每个线程组都有父节点。允许线程访问有关其自己的线程组的信息,但不允许访问有关其线程组的父线程组或任何其他线程组的信息。我们平常新建的线程都属于某个线程组,如果没有指定SecurityManager的话,默认新建的线程都属于当前创建线程所属的线程组。具体可以查看Thread源码的构造函数。
 
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 private  void  init (ThreadGroup g, Runnable target, String name,                       long  stackSize, AccessControlContext acc,                       boolean  inheritThreadLocals)  {    	...省略部分代码         Thread  parent  =  currentThread();         SecurityManager  security  =  System.getSecurityManager();         if  (g == null ) {                          if  (security != null ) {                 g = security.getThreadGroup();             }                          if  (g == null ) {                 g = parent.getThreadGroup();             }         }         g.checkAccess();         if  (security != null ) {             if  (isCCLOverridden(getClass())) {                 security.checkPermission(SUBCLASS_IMPLEMENTATION_PERMISSION);             }         }         g.addUnstarted();         this .group = g;     	...省略部分代码     } 
 
ThreadGroup的成员变量 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 private  final  ThreadGroup parent;String name; int  maxPriority;boolean  destroyed;boolean  daemon;boolean  vmAllowSuspension;int  nUnstartedThreads  =  0 ;int  nthreads;Thread threads[]; int  ngroups;ThreadGroup groups[]; 
 
ThreadGroup的构造函数 ThreadGroup中一共定义了四个构造函数,其中两个为私有的两个为公共的
public ThreadGroup(String name) 1 2 3 public  ThreadGroup (String name)  {	this (Thread.currentThread().getThreadGroup(), name); } 
 
指定线程组的名称,父线程组为当前正在执行的线程
public ThreadGroup(ThreadGroup parent, String name) 1 2 3 public  ThreadGroup (ThreadGroup parent, String name)  {	this (checkParentAccess(parent), parent, name); } 
 
指定线程组的父线程组和线程组的名称,并检查父线程的访问许可
private ThreadGroup(Void unused, ThreadGroup parent, String name) 1 2 3 4 5 6 7 8 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 ); } 
 
两个公共的构造函数最终都是调用这个构造函数初始化的,指定了线程组的一些属性,都保持和父线程组一致,最后将该线程组添加到父线程组中。
private ThreadGroup() 1 2 3 4 5 private  ThreadGroup ()  {         this .name = "system" ;     this .maxPriority = Thread.MAX_PRIORITY;     this .parent = null ; } 
 
创建一个不在任何Thread组中的空Thread组。 此方法用于创建系统线程组。
ThreadGroup常用方法 public int activeCount() 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 public  int  activeCount ()  {    int  result;               int  ngroupsSnapshot;     ThreadGroup[] groupsSnapshot;     synchronized  (this ) {         if  (destroyed) {             return  0 ;         }         result = nthreads;         ngroupsSnapshot = ngroups;         if  (groups != null ) {             groupsSnapshot = Arrays.copyOf(groups, ngroupsSnapshot);         } else  {             groupsSnapshot = null ;         }     }     for  (int  i  =  0  ; i < ngroupsSnapshot ; i++) {         result += groupsSnapshot[i].activeCount();     }     return  result; } 
 
统计当前线程组内以及包含的子线程组内所有的线程数量
public int activeGroupCount() 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 public  int  activeGroupCount ()  {    int  ngroupsSnapshot;     ThreadGroup[] groupsSnapshot;     synchronized  (this ) {         if  (destroyed) {             return  0 ;         }         ngroupsSnapshot = ngroups;         if  (groups != null ) {             groupsSnapshot = Arrays.copyOf(groups, ngroupsSnapshot);         } else  {             groupsSnapshot = null ;         }     }     int  n  =  ngroupsSnapshot;     for  (int  i  =  0  ; i < ngroupsSnapshot ; i++) {         n += groupsSnapshot[i].activeGroupCount();     }     return  n; } 
 
统计当前线程组内以及包含的子线程组内所有的线程组数量
public final void destroy() 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 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 );     } } 
 
销毁此线程组及其所有子线程组。 此线程组内的线程必须全部执行完毕了,否则会抛出IllegalThreadStateException异常,最终将所有的成员变量初始化为null并且将此线程组从它的父线程组中删除。
public int enumerate(Thread list[]) 1 2 3 4 public  int  enumerate (Thread list[])  {	checkAccess(); 	return  enumerate(list, 0 , true ); } 
 
将此线程组及其子组中的每个活动线程复制到指定的数组中。注意如果数组长度不够大,只会复制数组长度大小这么多的线程到该数组中
public int enumerate(Thread list[], boolean recurse) 1 2 3 4 public  int  enumerate (Thread list[], boolean  recurse)  {	checkAccess(); 	return  enumerate(list, 0 , recurse); } 
 
将此线程组中的每个活动线程复制到指定的数组中。如果recurse为true代表递归的复制子线程组中所有的活动线程引用。注意如果数组长度不够大,只会复制数组长度大小这么多的线程到该数组中
这两个复制线程的方法都调用了一个私有的enumerate(Thread list[], int n, boolean recurse)方法,有兴趣的可以自己去研究一下源码,这里就不多做分析了。
public int enumerate(ThreadGroup list[]) 1 2 3 4 public  int  enumerate (ThreadGroup list[])  {	checkAccess(); 	return  enumerate(list, 0 , true ); } 
 
复制到此线程组及其子组中每个活动子组的指定数组引用。注意如果数组长度不够大,只会复制数组长度大小这么多的线程组到该数组中
public int enumerate(ThreadGroup list[], boolean recurse) 1 2 3 4 public  int  enumerate (ThreadGroup list[], boolean  recurse)  {	checkAccess(); 	return  enumerate(list, 0 , recurse); } 
 
将此线程组中的每个活动线程组复制到指定的数组中。 如果recurse为true,代表递归的复制子线程组中所有的活动线程组引用。注意如果数组长度不够大,只会复制数组长度大小这么多的线程组到该数组中
这两个复制线程组的方法都调用了一个私有的enumerate(ThreadGroup list[], int n, boolean recurse)方法,有兴趣的可以自己去研究一下源码,这里就不多做分析了。
public final void interrupt() 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 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();     } } 
 
中断此线程组中的所有线程。此方法会在此线程组及其所有子组中的所有线程上调用interrupt方法。有关interrupt方法的用法可以查看之前Thread常用方法 一文
public final boolean parentOf(ThreadGroup g) 1 2 3 4 5 6 7 8 public  final  boolean  parentOf (ThreadGroup g)  {    for  (; g != null  ; g = g.parent) {         if  (g == this ) {             return  true ;         }     }     return  false ; } 
 
判断此线程组是线程组参数还是其祖先线程组之一
public void uncaughtException(Thread t, Throwable e) 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 public  void  uncaughtException (Thread t, Throwable e)  {    if  (parent != null ) {         parent.uncaughtException(t, e);     } else  {         Thread.UncaughtExceptionHandler  ueh  =              Thread.getDefaultUncaughtExceptionHandler();         if  (ueh != null ) {             ueh.uncaughtException(t, e);         } else  if  (!(e instanceof  ThreadDeath)) {             System.err.print("Exception in thread \""                               + t.getName() + "\" " );             e.printStackTrace(System.err);         }     } } 
 
该方法在线程常用方法 一文中分析过,这里就不多做分析了。
例子 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 package  com.example.demo;import  java.util.Arrays;import  java.util.concurrent.ThreadFactory;public  class  DemoApplication  {    public  static  void  main (String[] args)  {         CustomizedThreadGroup  customizedThreadGroup  =  new  CustomizedThreadGroup ();         customizedThreadGroup.newThread(() -> {             Thread  thread  =  Thread.currentThread();             ThreadGroup  threadGroup  =  thread.getThreadGroup();             Thread[] threads = new  Thread [1 ];             System.out.println(thread.getName());             System.out.println(threadGroup);             System.out.println(threadGroup.getMaxPriority());             System.out.println(threadGroup.getParent());             System.out.println(threadGroup.activeCount());             System.out.println(threadGroup.activeGroupCount());             System.out.println(threadGroup.getParent().parentOf(threadGroup));             threadGroup.enumerate(threads);             System.out.println(Arrays.toString(threads));             System.out.println(1  / 0 );         }).start();     }     static  class  CustomizedThreadGroup  extends  ThreadGroup  implements  ThreadFactory  {         private  static  int  n  =  0 ;         private  synchronized  String nextThreadName ()  {             return  "CustomizedThreadGroup-"  + n++;         }         public  CustomizedThreadGroup ()  {             super ("CustomizedThreadGroup" );         }         @Override          public  Thread newThread (Runnable runnable)  {             return  new  Thread (this , runnable, nextThreadName());         }         @Override          public  void  uncaughtException (Thread t, Throwable e)  {             System.out.print("CustomizedThreadGroup里面的"  + t.getName() + "运行时出现了异常:" );             e.printStackTrace();         }     } } 
 
输出
1 2 3 4 5 6 7 8 9 10 11 CustomizedThreadGroup-0 com.example.demo.DemoApplication$CustomizedThreadGroup[name=CustomizedThreadGroup,maxpri=10] 10 java.lang.ThreadGroup[name=main,maxpri=10] 1 0 true [Thread[CustomizedThreadGroup-0,5,CustomizedThreadGroup]] CustomizedThreadGroup里面的CustomizedThreadGroup-0运行时出现了异常:java.lang.ArithmeticException: / by zero 	at com.example.demo.DemoApplication.lambda$main$0(DemoApplication.java:26) 	at java.lang.Thread.run(Thread.java:748) 
 
总结 本文主要介绍了ThreadGroup相关的一些概念和基本方法的使用,知道了每个线程都会属于一个线程组,默认是属于当前创建线程所属的线程组的。了解线程和线程组之间的关系能够帮助我们更好的编写线程相关的代码。