1.线程组介绍
在 Java 中,ThreadGroup 用于表示一组线程。通过 ThreadGroup,我们可以批量控制和管理多个线程,使得线程管理更加方便。
ThreadGroup 和 Thread 的关系就像它们的字面意思一样简单:每个线程 (Thread) 必定属于一个线程组 (ThreadGroup),线程不能脱离线程组而单独存在。
例如,方法 main() 中的线程名称是 main,如果在执行 new Thread() 时没有明确指定其 ThreadGroup,那么默认情况下,会将当前正在执行的线程(即父线程)的 ThreadGroup 设置为新线程的 ThreadGroup。
public class Demo {
public static void main(String[] args) {
Thread subThread = new Thread(() -> {
System.out.println("The name of the thread group where subThread is located is:" +
Thread.currentThread().getThreadGroup().getName());
System.out.println("The name of the current thread (subThread) is:" +
Thread.currentThread().getName());
});
subThread.start();
System.out.println("The thread group name of the thread in which the main() method is executed is: "
+ Thread.currentThread().getThreadGroup().getName());
System.out.println("The name of the current thread is:"
+ Thread.currentThread().getName());
}
}
//输出:
The thread group name of the thread in which the main() method is executed is: main
The name of the current thread is:main
The name of the thread group where subThread is located is:main
The name of the current thread (subThread) is:Thread-0
一个线程组可以包含多个线程,也可以包含多个子线程组。从结构上来看,线程组是一个树形结构,每个线程都属于一个线程组,线程组之间通过父子关系连接,最终追溯到一个根线程组,即系统线程组。
- JVM 创建的 系统线程组(system thread group)是用于处理 JVM 系统任务的一组线程,例如对象的销毁、垃圾回收(GC)等。
- 系统线程组的直接子线程组是 main 线程组,其中至少包含一个用于执行 main 方法的线程。
- 应用程序创建的所有线程组都是 main 线程组的子线程组。
我们可以查看 JVM 创建的 system 线程组以及 main 线程组。
public static void main(String[] args) {
ThreadGroup mainThreadGroup = Thread.currentThread().getThreadGroup();
ThreadGroup systemThreadGroup = mainThreadGroup.getParent();
System.out.println("The name of the parent thread group of the thread group where the current thread is located = " + systemThreadGroup.getName());
System.out.println("The name of the thread group where the current thread is located is = " + mainThreadGroup.getName());
}
//输出:
The name of the parent thread group of the thread group where the current thread is located = system
The name of the thread group where the current thread is located is = main
线程可以访问其所属的线程组的信息,但无法访问该线程组的父线程组或其他线程组的信息。
线程组构成
首先,让我们看看 ThreadGroup 源码中的成员变量。
public class ThreadGroup implements Thread.UncaughtExceptionHandler {
父线程组
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 提供的两个构造函数。我会在代码中添加一些注释,以帮助大家更好地理解它们的功能和用法。
// JVM启动时调用,创建根线程组。
private ThreadGroup() {
this .name = "system" ;
this .maxPriority = Thread.MAX_PRIORITY;
this .parent = null ;
}
//默认传入当前ThreadGroup作为父ThreadGroup,新线程组的父线程组就是当前运行线程的线程组。
public ThreadGroup(String name) {
this (Thread.currentThread().getThreadGroup(), name);
}
//传入名称创建线程组,父线程由客户端指定。
public ThreadGroup(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() 方法用于验证当前线程是否具备对线程组进行修改的权限。这是为了确保线程组的安全性和一致性。
下面我将演示这两个构造函数的使用示例:
public class ConstructDemo {
public static void main(String[] args) {
ThreadGroup subThreadGroup1 = new ThreadGroup("subThreadGroup1");
ThreadGroup subThreadGroup2 = new ThreadGroup(subThreadGroup1, "subThreadGroup2");
System.out.println("subThreadGroup1 parent name is: " + subThreadGroup1.getParent().getName());
System.out.println("subThreadGroup2 parent name is: " + subThreadGroup2.getParent().getName());
}
}
//输出:
subThreadGroup1 parent name is: main
subThreadGroup2 parent name is: subThreadGroup1
ThreadGroup包含的方法介绍
ThreadGroup 提供了许多有用的方法,下面简要介绍一下。
我们选择了一些方法通过代码来演示其用法。
public class ThreadGroupMethodCase {
public static 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();
int activeThreadCount = subgroup1.activeCount();
System.out.println("Active thread in " + subgroup1.getName() + " thread group: " + activeThreadCount);
ThreadGroup subgroup2 = new ThreadGroup("subgroup2");
Thread t4 = new Thread(subgroup2, "t4 in subgroup2");
ThreadGroup currentThreadGroup = Thread.currentThread().getThreadGroup();
int activeGroupCount = currentThreadGroup.activeGroupCount();
System.out.println("Active thread groups in " + currentThreadGroup.getName() + " thread group: " + activeGroupCount);
System.out.println("Prints information about currentThreadGroup to the standard output:");
currentThreadGroup.list();
}
}
//输出:
Active thread in subgroup1 thread group: 2
Active thread groups in main thread group: 2
Prints information about currentThreadGroup to the standard output:
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]
这里要注意的一点是,在输出当前线程组中的活动线程数时,只计算状态为 RUNNABLE、BLOCKED、WAITING 或 TIMED_WAITING 的线程,不包括状态为 NEW 或 TERMINATED 的线程。
因此,当我们调用 subgroup1.activeCount() 时,它实际上只会返回当前活动的线程数。举例来说,如果线程 t1 已经结束,而线程 t3 尚未启动,那么只有线程 t2 被计算在内。