在 Java 中,ThreadGroup用于表示一个线程组。我们可以使用ThreadGroup来批量控制线程,更方便地管理线程。
ThreadGroup和Thread之间的关系非常简单,就像它们的字面意思一样:每个Thread必然存在于一个ThreadGroup中,一个Thread不能独立于ThreadGroup存在。
执行main()方法的线程名字是main,如果你在执行new Thread()时没有显式指定一个ThreadGroup,那么默认会将父线程(当前正在执行new Thread()的线程)的ThreadGroup设置为子线程的ThreadGroup。
示例代码:
publicclass Demo {publicstatic void main(String[]args){ Thread subThread=new Thread(()->{ System.out.println("子线程所在的线程组名称是:"+Thread.currentThread().getThreadGroup().getName());System.out.println("当前线程(子线程)的名称是:"+Thread.currentThread().getName());});subThread.start();System.out.println("执行 main() 方法的线程所在的线程组名称是:"+Thread.currentThread().getThreadGroup().getName());System.out.println("当前线程的名称是:"+Thread.currentThread().getName());} }
输出:
执行main()方法的线程所在的线程组名称是: main 当前线程的名称是: main 子线程所在的线程组名称是: main 当前线程(子线程)的名称是: Thread-0
线程组是父子结构的,一个线程组可以包含其他线程组,也可以有其他子线程组。从结构上看,线程组是一个树形结构,每个线程属于一个线程组,而该线程组又有一个父线程组,依此类推,最终可以追溯到根线程组,即System线程组。
结构如下所示:
图片
JVM 创建的system线程组是一组用于处理 JVM 系统任务的线程,比如对象销毁、垃圾回收(GC)等。
system线程组的直接子线程组是main线程组,它至少包含一个执行main方法的main线程。
main线程组的子线程组是由应用程序创建的线程组。
你可以在main方法中看到 JVM 创建的system线程组和main线程组:
publicstatic void main(String[]args){ ThreadGroup mainThreadGroup=Thread.currentThread().getThreadGroup();ThreadGroup systemThreadGroup=mainThreadGroup.getParent();System.out.println("当前线程所在的线程组的父线程组名称 = "+systemThreadGroup.getName());System.out.println("当前线程所在的线程组名称 = "+mainThreadGroup.getName());}
输出:
当前线程所在的线程组的父线程组名称=system 当前线程所在的线程组名称=main
一个线程可以访问它所属线程组的信息,但不能访问它所属线程组的父线程组或其他线程组的信息。
首先,我们来看一下ThreadGroup源码中的成员变量。
publicclass ThreadGroup implements Thread.UncaughtExceptionHandler { private final ThreadGroup parent;// 父线程组String name;intmaxPriority;booleandestroyed;booleandaemon;booleanvmAllowSuspension;intnUnstartedThreads=0;intnthreads;// 子线程数量Thread threads[];// 子线程数组intngroups;// 子线程组数量ThreadGroup groups[];// 子线程组数组}
接下来,我们看一下java.lang.ThreadGroup提供的两个构造函数,我添加了一些注释以便理解。
// 当 JVM 启动时,调用此构造函数创建根线程组。private ThreadGroup(){ this.name="system";this.maxPriority=Thread.MAX_PRIORITY;this.parent=null;}// 默认情况下,传入当前 ThreadGroup 作为父 ThreadGroup。新线程组的父线程组是当前运行线程的线程组。publicThreadGroup(String name){ this(Thread.currentThread().getThreadGroup(),name);}// 传入名称创建线程组,父线程组由客户端指定。publicThreadGroup(ThreadGroup parent,String name){ this(checkParentAccess(parent),parent,name);}// 主要的私有构造函数,大多数参数从父线程组继承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);}
checkParentAccess()方法用于判断当前运行的线程是否有权限修改线程组。
以下代码演示了这两个构造函数的用法:
publicclass ConstructDemo {publicstatic void main(String[]args){ ThreadGroup subThreadGroup1=new ThreadGroup("subThreadGroup1");ThreadGroup subThreadGroup2=new ThreadGroup(subThreadGroup1,"subThreadGroup2");System.out.println("subThreadGroup1 的父线程组名称是:"+subThreadGroup1.getParent().getName());System.out.println("subThreadGroup2 的父线程组名称是:"+subThreadGroup2.getParent().getName());} }
输出:
subThreadGroup1的父线程组名称是: main subThreadGroup2的父线程组名称是: subThreadGroup1
ThreadGroup提供了许多有用的方法,下面简要介绍其中一些。
方法 | 描述 |
void checkAccess() | 判断当前运行的线程是否有权限修改线程组。 |
int activeCount() | 返回线程组及其子组中活动线程的估计数量。 |
int activeGroupCount() | 返回线程组及其子组中活动线程组的估计数量。 |
void destroy() | 销毁线程组及其所有子组。 |
int enumerate(Thread[] list) | 将线程组及其子组中的所有活动线程复制到指定的数组中。 |
int getMaxPriority() | 返回线程组的最大优先级。 |
String getName() | 返回线程组的名称。 |
ThreadGroup getParent() | 返回线程组的父线程组。 |
void interrupt() | 中断线程组中的所有线程。 |
boolean isDaemon() | 判断线程组是否是守护线程组。 |
void setDaemon(boolean daemon) | 设置线程组的守护状态。 |
boolean isDestroyed() | 判断线程组是否已被销毁。 |
void list() | 将线程组的信息打印到标准输出。 |
boolean parentOf(ThreadGroup g) | 判断线程组是否是参数线程组或其祖先线程组。 |
void suspend() | 挂起线程组中的所有线程。 |
void resume() | 恢复线程组中所有被挂起的线程。 |
void setMaxPriority(int prt) | 设置线程组的最大优先级。 |
void stop() | 停止线程组中的所有线程。 |
String toString() | 返回线程组的字符串表示。 |
我们选择其中一些方法来演示用法。
publicclass ThreadGroupMethodCase {publicstatic void main(String[]args)throws InterruptedException { ThreadGroup subgroup1=new ThreadGroup("subgroup1");Thread t1=new Thread(subgroup1,"t1 in subgroup1");Thread t2=new Thread(subgroup1,"t2 in subgroup1");Thread t3=new Thread(subgroup1,"t3 in subgroup1");t1.start();Thread.sleep(50);t2.start();intactiveThreadCount=subgroup1.activeCount();System.out.println("线程组 "+subgroup1.getName()+" 中的活动线程数量:"+activeThreadCount);ThreadGroup subgroup2=new ThreadGroup("subgroup2");Thread t4=new Thread(subgroup2,"t4 in subgroup2");ThreadGroup currentThreadGroup=Thread.currentThread().getThreadGroup();intactiveGroupCount=currentThreadGroup.activeGroupCount();System.out.println("线程组 "+currentThreadGroup.getName()+" 中的活动线程组数量:"+activeGroupCount);System.out.println("将当前线程组的信息打印到标准输出:");currentThreadGroup.list();} }
输出:
线程组 subgroup1 中的活动线程数量:2线程组 main 中的活动线程组数量:2将当前线程组的信息打印到标准输出: java.lang.ThreadGroup[name=main,maxpri=10]Thread[main,5,main]java.lang.ThreadGroup[name=subgroup1,maxpri=10]java.lang.ThreadGroup[name=subgroup2,maxpri=10]
这里有一个有趣的地方:当输出当前线程组中的活动线程数量时,实际上并没有计算状态为NEW和TERMINATED的线程。所以当输出subgroup1.activeCount()时,实际上只有一个活动线程,即t2,因为t1已经结束,而t3还没有启动。
简单来说,线程组是一个树形结构,每个线程组下可以有多个线程或多个线程组。线程组可以用于统一控制线程的优先级、检查线程的权限等。