线程组 Thread Group

目录

前言

正文

1.线程对象关联线程组:一级关联 

2.线程对象关联线程组:多级关联 

3.线程组自动归属特性 

4.获取根线程组 

5.线程组内加线程组  

6.组内的线程批量停止 

7.递归取得与非递归取得组内对象 

8. Thread.activeCount() 方法的使用 

9. Thread.enumerate(Thread tarray[]) 方法的使用

10.补充  

总结


前言

为了方便对某些具有相同功能的线程进行管理,我们可以把线程归属到某一个线程组。线程组中可以有线程对象、线程,类似于树的形式。


正文

线程组的作用是可以批量地管理线程或线程对象,有效地对线程或线程对象进行组织 。

1.线程对象关联线程组:一级关联 

所谓的一级关联就是父对象中有子对象,但并不创建子孙对象,这种情况经常出现在开发中。

public class groupAddThread {static class Task implements Runnable{@Overridepublic void run() {try{while(!Thread.currentThread().isInterrupted()){System.out.println("ThreadName"+Thread.currentThread().getName());Thread.sleep(3000);}} catch (InterruptedException e) {e.printStackTrace();}}}public static void main(String[] args) {Task task = new Task();ThreadGroup group = new ThreadGroup("shenyang的线程组");Thread athread = new Thread(group,task);Thread bthread = new Thread(group,task);athread.start();bthread.start();System.out.println("活动的线程数为:"+group.activeCount());System.out.println("线程组的名称为:"+group.getName());}
}

在代码中将 athread 和 bthread 对象与线程组关联,然后对 athread 和 bthread 对象执行 start() 方法,之后执行 task 中的 run() 方法。

程序结果如图:

控制台打印出线程组中的 2 个线,以及线程组的名称。另外,2 个线程一直运行,并且每隔 3 秒打印日志。  

2.线程对象关联线程组:多级关联 

所谓多级关联就是父对象中有子对象,子对象中再创建子对象,也就出现了子孙对象。但是,这种写法在开发中并不常见。设计非常复杂的线程树结构不利于线程对象的管理,但 JDK 支持多级关联的线程结构。实现多级关联的关键是 ThreadGroup 类的构造方法: 

public ThreadGroup(ThreadGroup parent, String name) 

新建测试用例

public class groupAddThreadMethodLevel {public static void main(String[] args) {//在 main 组中添加线程组 A,然后在线程组 A 添加线程对象 z。//方法 activeGroupCount() 和 activeCount()的值不是固定的。//而是系统的一个快照ThreadGroup mainGroup = Thread.currentThread().getThreadGroup();//取得 main 主线程所在的线程组ThreadGroup group = new ThreadGroup(mainGroup,"A");Runnable runnable = new Runnable() {@Overridepublic void run() {try {System.out.println("runMethod!");Thread.sleep(10000);} catch (InterruptedException e) {e.printStackTrace();}}};Thread newThread = new Thread(group,runnable);newThread.setName("z");newThread.start();//线程必须是程序启动后才归属到线程组 A 中,因为在调用 start() 方法时会调用//group.add(this);ThreadGroup[] listGroup = new ThreadGroup[Thread.currentThread().getThreadGroup().activeGroupCount()];Thread.currentThread().getThreadGroup().enumerate(listGroup);System.out.println("main 线程中有多少个子线程组: "+listGroup.length+" 名字为:"+listGroup[0].getName());Thread[] listThread = new Thread[listGroup[0].activeCount()];listGroup[0].enumerate(listThread);System.out.println(listThread[0].getName());}
}

运行结果如图

本程序代码的结构是在 main 线程组中创建一个新线程组,然后在该新线程组中添加了线程,并取得相关信息。

3.线程组自动归属特性 

自动归属就是自动归到当前线程组中(在没有指定线程组的情况下)。

新建测试用例

package org.example.Group;public class Run {public static void main(String[] args) {//方法 activeGroupCount() 取得当前线程组对象中的子线程组数量//方法 enumerate() 的作用是将线程组中的子线程组以复制的形式// 复制到 ThreadGroup[] 数组对象中System.out.println("A处线程:"+Thread.currentThread().getName()+" 所属的线程组名为:"+Thread.currentThread().getThreadGroup().getName()+" "+" 中有线程组数量:"+Thread.currentThread().getThreadGroup().activeGroupCount());ThreadGroup group = new ThreadGroup("新的组"); // 自动加入到 main 组中System.out.println("B处线程:"+Thread.currentThread().getName()+" 所属的线程组名为:"+Thread.currentThread().getThreadGroup().getName()+" "+" 中有线程组数量:"+Thread.currentThread().getThreadGroup().activeGroupCount());ThreadGroup[] threadGroups = new ThreadGroup[Thread.currentThread().getThreadGroup().activeGroupCount()];Thread.currentThread().getThreadGroup().enumerate(threadGroups);for (int i = 0; i < threadGroups.length; i++) {System.out.println("第一个线程组名称为:"+threadGroups[i].getName());}}
}

运行结果如图:

本实验要证明的是,在实例化 1 个 ThreadGroup 线程组 x 时,如果不指定所属的线程组,线程组 x 会自动归到当前线程对象所属的线程组中,也就是隐式地在线程组中添加了一个子线程组,所以在控制台打印的线程组数量值由 0 变成了 1 。

线程也具有这种特性,即自动归属到创建该线程的线程所在的组中。 

package org.example.Group;public class Thread_ThreadGroup {static class MyThread extends Thread{@Overridepublic void run() {System.out.println("当前线程:"+Thread.currentThread().getName()+"   所在的线程组: "+Thread.currentThread().getThreadGroup().getName());}}public static void main(String[] args) {MyThread myThread = new MyThread();myThread.setName("myThread");myThread.start();}
}
public class Thread_ThreadGroup {static class Task implements Runnable{@Overridepublic void run() {System.out.println("当前线程:"+Thread.currentThread().getName()+"   所在的线程组: "+Thread.currentThread().getThreadGroup().getName());Thread thread = new Thread(() -> System.out.println("当前线程:"+Thread.currentThread().getName()+"   所在的线程组: "+Thread.currentThread().getThreadGroup().getName()));thread.start();}}public static void main(String[] args) {Task task = new Task();ThreadGroup group = new ThreadGroup("子线程组");Thread myThread = new Thread(group,task);myThread.setName("myThread");myThread.start();}
}

运行结果如图

    

4.获取根线程组 

JVM 的根线程组是 system,下面来进行测试。 

public class RunT {public static void main(String[] args) {System.out.println("线程: " + Thread.currentThread().getName()+ "所在的线程组名称为:"+ Thread.currentThread().getThreadGroup().getName());System.out.println("main 线程所在的线程组的父线程组的名称是:"+ Thread.currentThread().getThreadGroup().getParent().getName());System.out.println("main 线程所在的线程组的父线程组的父线程组的名称是:"+ Thread.currentThread().getThreadGroup().getParent().getParent().getName());}
}

运行结果如图

证明 JVM 的根线程组就是 system,再取父线程组则出现空异常。 

5.线程组内加线程组  

本实验显示地在一个线程组中添加了一个子线程组

创建测试用例

package org.example.Group;public class RunF {public static void main(String[] args) {System.out.println("线程组名称:"+ Thread.currentThread().getThreadGroup().getName());System.out.println("线程组种活动的线程数量:"+ Thread.currentThread().getThreadGroup().activeCount());System.out.println("线程组中线程组的数量 - 加之前:"+ Thread.currentThread().getThreadGroup().activeGroupCount());ThreadGroup newGroup = new ThreadGroup(Thread.currentThread().getThreadGroup(), "newGroup");System.out.println("线程组中线程组的数量 - 加之后:"+ Thread.currentThread().getThreadGroup().activeGroupCount());System.out.println("父线程组名称:"+ Thread.currentThread().getThreadGroup().getParent().getName() + "线程组中活跃线程组:");}
}

运行结果如图

6.组内的线程批量停止 

使用线程组 ThreadGroup 的优点是可以批量处理本组内线程对象,比如可以批量中断组中的线程。 

新建测试用例

package org.example.Group;public class groupInnerStop {static class MyThread extends Thread {public MyThread(ThreadGroup group, String name) {super(group, name);}@Overridepublic void run() {System.out.println("ThreadName=" + Thread.currentThread().getName()+ "准备开始死循环");while (!this.isInterrupted()) {}System.out.println("ThreadName=" + Thread.currentThread().getName()+ "结束:)");}}public static void main(String[] args) {try{ThreadGroup group = new ThreadGroup("我的线程组");for (int i = 0; i < 5; i++) {MyThread thread = new MyThread(group,"线程 "+(i+1));thread.start();}Thread.sleep(5000);group.interrupt();System.out.println("调用了 interrupt() 方法");} catch (InterruptedException e) {System.out.println("停了停了!");e.printStackTrace();}}}

运行结果如图

通过将线程归属到线程组,我们可以实现当调用线程组 ThreadGroup() 的 interrupt() 方法时中断该组中所有正在运行的线程。 

7.递归取得与非递归取得组内对象 

使用类 ThreadGroup 的 activeGroupCount() 方法取得子孙组的数量。然后根据数量创建 ThreadGroup[] 数组,最后通过 enumerate(ThreadGroup[] list, boolean recurse) 方法决定递归或者非递归将线程组对象复制(传引用)到该线程组数组中。

package org.example.Group;public class groupRecurseTest {public static void main(String[] args) {ThreadGroup mainGroup = Thread.currentThread().getThreadGroup();ThreadGroup groupA = new ThreadGroup(mainGroup, "A");Runnable runnable = new Runnable() {@Overridepublic void run() {try {System.out.println("runMethod!");Thread.sleep(10000);} catch (InterruptedException e) {e.printStackTrace();}}};ThreadGroup groupB = new ThreadGroup(groupA, "B");//分配空间 但不一定全部用完ThreadGroup[] listGroup1 = new ThreadGroup[Thread.currentThread().getThreadGroup().activeGroupCount()];//传入 true 时递归取得子线程组及子孙组Thread.currentThread().getThreadGroup().enumerate(listGroup1, true);for (int i = 0; i < listGroup1.length; i++) {if (listGroup1[i] != null) {System.out.println(listGroup1[i].getName());}}System.out.println("----------------------------");ThreadGroup[] listGroup2 = new ThreadGroup[Thread.currentThread().getThreadGroup().activeGroupCount()];Thread.currentThread().getThreadGroup().enumerate(listGroup2, false);for (int i = 0; i < listGroup2.length; i++) {if (listGroup2[i] != null) {System.out.println(listGroup2[i].getName());}}}
}

程序运行结果如图

  

同时需要值得注意的是,如果不传入是否递归的参数,也就是使用 enumerate(Thread[] list) 方法默认情况下是会递归的,详情可见 JDK 源码。默认传入的参数为 true

    public int enumerate(ThreadGroup list[]) {checkAccess();return enumerate(list, 0, true);}

enumerate(ThreadGroup[] list, boolean recurse) 的源码,两个方法最终都会调用 private int enumerate(ThreadGroup list[], int n, boolean recurse) 方法。该方法私有,是 ThreadGroup 类的内部实现,原理就是根据传入的 recurse 参数使用递归算法进行线程组的遍历。

    public int enumerate(ThreadGroup list[], boolean recurse) {checkAccess();return enumerate(list, 0, recurse);}

无论是否使用 recurse 参数进行方法的选择,都会使用  checkAccess() 方法进行检查,简单了解一下该方法,该方法用于确定当前线程是否有权限修改当前线程组。如果存在安全管理器,会调用其 checkAccess方法,以当前线程组作为参数。如果未获得访问权限,则会抛出SecurityException异常。


public final void checkAccess() {SecurityManager security = System.getSecurityManager();if (security != null) {security.checkAccess(this);}}
//此方法是上面方法的最后一步的调用
public void checkAccess(ThreadGroup g) {if (g == null) {throw new NullPointerException("thread group can't be null");}if (g == rootGroup) {checkPermission(SecurityConstants.MODIFY_THREADGROUP_PERMISSION);} else {// just return}

8. Thread.activeCount() 方法的使用 

public static int activeCount() 方法的作用是返回当前线程所在的线程组中活动线程的数目。 

    public static int activeCount() {return currentThread().getThreadGroup().activeCount();}

创建测试用例

package org.example.Group;public class RunC {public static void main(String[] args) {System.out.println(Thread.activeCount());}
}

运行结果如图

可见当前线程所在的线程组中有 2活动线程,一般应为 1 个,这里可能会因为不同的 IDE 造成的结果不同,可能由集成的 IDE 创建一个用于监听热加载的线程、用于处理垃圾回收的线程,等等。 

9. Thread.enumerate(Thread tarray[]) 方法的使用

public static int enumerate(Thread tarray[]) 方法的作用是将当前线程所在的线程组及其子组中每一个活动线程复制到指定的数组中。该方法只调用当前线程所在的线程所在的线程组的 enumerate 方法,且带有数组参数,

    public static int enumerate(Thread tarray[]) {return currentThread().getThreadGroup().enumerate(tarray);}

创建测试用例

public class RunE {public static void main(String[] args) {//这里是通过实例引用访问 static 成员 'java.lang.Thread.activeCount()'//正常 Thread.activeCount() 就可以了,为了更直观的感受Thread[] threads = new Thread[Thread.currentThread().activeCount()];Thread.enumerate(threads);for (int i = 0; i < threads.length; i++) {System.out.println(threads[i].getName());}}
}

 运行结果如图

同时,也证明了上一个实验中的结论:即 2 个活动线程中含有一个监控线程 Monitor Ctrl-Break ,当然这个线程是 IntelliJ IDEA 特有的用来监听旁边停止按钮所产生的中断信号的线程。


10.补充  

补充关于 enumerate()

int

enumerate(Thread[] list)

将此线程组及其子组中的每个活动线程复制到指定的数组中。

int

enumerate(Thread[] list, boolean recurse)

将此线程组中的每个活动线程复制到指定的数组中。

int

enumerate(Thread[] list, boolean recurse)

复制到该线程组及其子组中每个活动子组的指定数组引用。

int

enumerate(ThreadGroup[] list, boolean recurse)

复制到该线程组中每个活动子组的指定数组引用。

补充关于 activeGroupCount() ;

activeGroupCount() 是 Java ThreadGroup 类的一个方法。此方法用于估算调用该方法的线程组中活动线程组的数量。"活动线程组"是指那些未被销毁且不是守护线程组的线程组。

要注意的是,activeGroupCount() 方法返回的值是一个估算值,因为在一个线程组的子线程组中的线程可能在计算过程中开始或结束。你不能依靠这个值进行精确的操作,但它对于监控和调试线程行为很有价值。


总结

线程组是 Java 中的一个概念,用于管理线程集合。一个线程组可以包含线程,并且可以是另一个线程组的子项。线程组形成的是一个树状结构,除了系统线程组(根线程组)之外,每个线程组都有一个父线程组。线程可以访问一些有关自己所属线程组的信息,比如线程组的名称,以及线程组中活动线程的数目等。

加油!!!!!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/231865.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

1990-2021年上市公司排污费和环境保护税数据

1990-2021年上市公司排污费和环境保护税数据 1、时间&#xff1a;1990-2021年 2、指标&#xff1a; 证券代码、会计期间、year、month、行业、应缴排污费/环境保护税、其中&#xff1a;大气污染物、其中&#xff1a;水污染物、其中&#xff1a;固体废物、其中&#xff1a;噪…

XUbuntu22.04之隐藏顶部任务栏(一百九十四)

简介&#xff1a; CSDN博客专家&#xff0c;专注Android/Linux系统&#xff0c;分享多mic语音方案、音视频、编解码等技术&#xff0c;与大家一起成长&#xff01; 优质专栏&#xff1a;Audio工程师进阶系列【原创干货持续更新中……】&#x1f680; 优质专栏&#xff1a;多媒…

单片机_RTOS_架构

一. RTOS的概念 // 经典单片机程序 void main() {while (1){喂一口饭();回一个信息();} } ------------------------------------------------------ // RTOS程序 喂饭() {while (1){喂一口饭();} }回信息() {while (1){回一个信息();} }void main() {create_task(喂饭);cr…

小航助学题库蓝桥杯题库c++选拔赛(23年8月)(含题库教师学生账号)

需要在线模拟训练的题库账号请点击 小航助学编程在线模拟试卷系统&#xff08;含题库答题软件账号&#xff09; 需要在线模拟训练的题库账号请点击 小航助学编程在线模拟试卷系统&#xff08;含题库答题软件账号&#xff09;

[VNCTF 2023] web刷题记录

文章目录 象棋王子电子木鱼BabyGo 象棋王子 考点&#xff1a;前端js代码审计 直接查看js源码&#xff0c;搜一下alert 丢到控制台即可 电子木鱼 考点&#xff1a;整数溢出 main.rs我们分段分析 首先这段代码是一个基于Rust的web应用程序中的路由处理函数。它使用了Rust的异步…

适用于 Windows 的最佳电脑数据恢复软件是什么?

数据丢失是数字世界中令人不快的一部分&#xff0c;它会在某一时刻影响许多计算机用户。很容易意外删除一些重要文件&#xff0c;这可能会在您努力恢复它们时带来不必要的压力。幸运的是&#xff0c;数据恢复软件可以帮助恢复已删除的文件&#xff0c;即使您没有备份它们。这是…

机器学习笔记 - 3D数据的常见表示方式

一、简述 从单一角度而自动合成3D数据是人类视觉和大脑的基本功能,这对计算机视觉算法来说是比较难的。但随着LiDAR、RGB-D 相机(RealSense、Kinect)和3D扫描仪等3D传感器的普及和价格的降低,3D 采集技术的最新进展取得了巨大飞跃。与广泛使用的 2D 数据不同,3D 数据具有丰…

【笔记】windows+pytorch:部署一下stable diffusion和NeRF

之前都是 *nix 环境使用 pytorch&#xff0c;这次尝试了一下windows。 我们来部署下流行性高的stable diffusion和我觉得实用性比stable diffusion高多了的NeRF Stable Diffusion 其实&#xff0c;我也不知道要写啥&#xff0c;都是按照步骤做就好了&#xff0c;后面等有时间…

防火墙之iptables

iptables概述 1.Linux 系统的防火墙 &#xff1a;IP信息包过滤系统&#xff0c;它实际上由两个组件netfilter 和 iptables组成。 2.主要工作在网络层&#xff0c;针对IP数据包。体现在对包内的IP地址、端口、协议等信息的处理上。 -netfilter/iptables关系&#xff1a; netfil…

【C++】单链表——单链表的基本操作

1、单链表的定义 由于顺序表的插入删除操作需要移动大量的元素&#xff0c;影响了运行效率&#xff0c;因此引入了线性表的链式存储——单链表。单链表通过一组任意的存储单元来存储线性表中的数据元素&#xff0c;不需要使用地址连续的存储单元&#xff0c;因此它不要求在逻辑…

运维知识点-openResty

openResty 企业级实战——畅购商城SpringCloud-网站首页高可用解决方案-openRestynginxlua——实现广告缓存测试企业级实战——畅购商城SpringCloud-网站首页高可用解决方案-openRestynginxlua——OpenResty 企业级实战——畅购商城SpringCloud-网站首页高可用解决方案-openRes…

基于深度学习的表情动作单元识别综述

论文标题&#xff1a;基于深度学习的表情动作单元识别综述 作者&#xff1a;邵志文1&#xff0c;2&#xff0c;周 勇1&#xff0c;2&#xff0c;谭 鑫3&#xff0c;马利庄3&#xff0c;4&#xff0c;刘 兵1&#xff0c;2&#xff0c;姚 睿1&#xff0c;2 发表日期&#xff1a…