Java并发编程:什么是线程组?它有什么作用?

开发 前端
线程组是一个树形结构,每个线程组下可以有多个线程或多个线程组。线程组可以用于统一控制线程的优先级、检查线程的权限等。​

线程组简介

在 Java 中,ThreadGroup用于表示一个线程组。我们可以使用ThreadGroup来批量控制线程,更方便地管理线程。

ThreadGroup和Thread之间的关系非常简单,就像它们的字面意思一样:每个Thread必然存在于一个ThreadGroup中,一个Thread不能独立于ThreadGroup存在。

执行main()方法的线程名字是main,如果你在执行new Thread()时没有显式指定一个ThreadGroup,那么默认会将父线程(当前正在执行new Thread()的线程)的ThreadGroup设置为子线程的ThreadGroup。

示例代码:

public class Demo {
    public static 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线程组。

结构如下所示:

图片图片

  1. JVM 创建的system线程组是一组用于处理 JVM 系统任务的线程,比如对象销毁、垃圾回收(GC)等。
  2. system线程组的直接子线程组是main线程组,它至少包含一个执行main方法的main线程。
  3. main线程组的子线程组是由应用程序创建的线程组。

你可以在main方法中看到 JVM 创建的system线程组和main线程组:

public static 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源码中的成员变量。

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[]; // 子线程组数组
}

接下来,我们看一下java.lang.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 的父线程组名称是:" +
        subThreadGroup1.getParent().getName());
        System.out.println("subThreadGroup2 的父线程组名称是:" +
        subThreadGroup2.getParent().getName());
    }
}

输出:

subThreadGroup1的父线程组名称是: main
subThreadGroup2的父线程组名称是: subThreadGroup1

ThreadGroup 包含的方法

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()

返回线程组的字符串表示。

我们选择其中一些方法来演示用法。

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("线程组 " + subgroup1.getName() + " 中的活动线程数量:" + 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("线程组 " + 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还没有启动。

总结

简单来说,线程组是一个树形结构,每个线程组下可以有多个线程或多个线程组。线程组可以用于统一控制线程的优先级、检查线程的权限等。


责任编辑:武晓燕 来源: 程序猿技术充电站
相关推荐

2023-09-19 16:37:47

网络

2024-12-25 16:04:53

2020-11-19 07:38:57

边缘计算

2022-02-16 22:37:00

流式SQLSQL数据库

2022-08-03 09:00:00

安全漏洞UPnP

2017-03-20 15:50:55

以太网网络工业

2022-05-30 07:34:33

三范式Java

2023-12-11 08:03:01

Java线程线程组

2023-12-21 21:39:44

2023-09-01 13:49:00

内存进程线程

2011-12-29 13:31:15

Java

2023-01-26 19:52:30

2022-05-07 07:35:44

工具读写锁Java

2024-07-08 00:01:00

GPM模型调度器

2019-05-06 11:12:18

Redis高并发单线程

2011-11-11 15:47:22

JavaScript

2021-03-09 08:50:58

JavaScript前端作用域

2011-07-05 17:19:47

元编程

2025-01-10 07:10:00

2022-07-13 07:06:47

HTTPSHTTP协议
点赞
收藏

51CTO技术栈公众号