当前位置: 首页 > news >正文

第6章 ThreadGroup详细讲解(Java高并发编程详解:多线程与系统设计)

1.ThreadGroup 与 Thread

        在Java程序中, 默认情况下, 新的线程都会被加入到main线程所在的group中, main线程的group名字同线程名。如同线程存在父子关系一样, Thread Group同样也存在父子关系。图6-1就很好地说明了父子thread、父子thread Group以及thread和group之间的层次关系

2.创建ThreadGroup

创建Thread Group的语法如下:

public Thread Group(String name)
public Thread Group(Thread Group parent, String name)

创建Thread Group的语法非常简单, 可通过上面某个构造函数来创建, 第一个构造函数为Thread Group赋予了名字, 但是该Thread Group的父Thread Group是创建它的线程所在的Thread Group; 第二个Thread Group的构造函数赋予group名字的同时又显式地指定了父Group。


public class TestThreadGroup {public static void main(String[] args) {ThreadGroup currentGroup = Thread.currentThread().getThreadGroup();ThreadGroup group1 = new ThreadGroup("group1");System.out.println(group1.getParent() == currentGroup);ThreadGroup group2 = new ThreadGroup(group1, "Group2");System.out.println(group2.getParent() == group1);}
}

3.复制Thread数组和ThreadGroup数组

3,1复制Thread数组

public int enumerate(Thread[] list);
public int enumerate(Thread[] list, boolean recurse);

上述两个方法, 会将Thread Group中的active线程全部复制到Thread数组中, 其中recurse参数如果为true, 则该方法会将所有子group中的active线程都递归到Thread数组中, enumerate(Thread[] list) 实际上等价于enumerate(Thread[] true) , 上面两个方法都调用了Thread Group的私有方法enumerate:

 private int enumerate(Thread list[], int n, boolean recurse) {int ngroupsSnapshot = 0;ThreadGroup[] groupsSnapshot = null;synchronized (this) {if (destroyed) {return 0;}int nt = nthreads;if (nt > list.length - n) {nt = list.length - n;}for (int i = 0; i < nt; i++) {if (threads[i].isAlive()) {list[n++] = threads[i];}}if (recurse) {ngroupsSnapshot = ngroups;if (groups != null) {groupsSnapshot = Arrays.copyOf(groups, ngroupsSnapshot);} else {groupsSnapshot = null;}}}if (recurse) {for (int i = 0 ; i < ngroupsSnapshot ; i++) {n = groupsSnapshot[i].enumerate(list, n, true);}}return n;}

举一例:enumerate方法的使用

import java.util.concurrent.TimeUnit;public class TestThreadGroup {public static void main(String[] args) throws InterruptedException {ThreadGroup myGroup = new ThreadGroup("mygroup");Thread th  = new Thread(myGroup,()-> {while(true) {try {TimeUnit.SECONDS.sleep(1);} catch (InterruptedException e) {e.printStackTrace();}}},"MyThread");th.start();TimeUnit.MILLISECONDS.sleep(2);ThreadGroup mainGroup = Thread.currentThread().getThreadGroup();Thread[] list = new Thread[mainGroup.activeCount()];int recuseSize = mainGroup.enumerate(list);System.out.println(recuseSize);recuseSize = mainGroup.enumerate(list,false);System.out.println(recuseSize);}
}

上面的代码运行之后, 最后一个输出会比第一个少1, 那是因为代码中将递归recurse设置为了false, my Group中的线程将不会包含在内。

3.2 复制ThreadGroup数组

public int enumerate(Thread Group[] list);
public int enumerate(Thread Group[] list, boolean recurse);

和复制Thread数组类似, 上述两个方法, 主要用于复制当前Thread Group的子Group,同样recurse会决定是否以递归的方式复制。

import java.util.concurrent.TimeUnit;public class TestCopyThreadGroup {public static void main(String[] args) throws InterruptedException {ThreadGroup myGroup1 = new ThreadGroup("MyGroup1");ThreadGroup myGroup2 = new ThreadGroup(myGroup1, "MyGroup2");TimeUnit.MILLISECONDS.sleep(2);ThreadGroup mainGroup = Thread.currentThread().getThreadGroup();ThreadGroup[] list = new ThreadGroup[mainGroup.activeCount()];int recurseSize = mainGroup.enumerate(list);System.out.println(recurseSize);recurseSize = mainGroup.enumerate(list,false);System.out.println(recurseSize);}
}

在代码清单6-3中, my Group 1的父group为main Group, 而my Group 2的父group为my Group 1, 因此上述的代码运行之后, 递归复制的结果为2, 不递归的情况下为1。

4.ThreadGroup操作

4.1ThreadGroup的基本操作

import java.util.concurrent.TimeUnit;public class ThreadGroupBasic {public static void main(String[] args) {ThreadGroup group = new ThreadGroup("group1");Thread thread = new Thread(group,() -> {while(true) {try {TimeUnit.SECONDS.sleep(1);} catch (InterruptedException e) {e.printStackTrace();}}}, "thread");thread.setDaemon(true);thread.start();ThreadGroup mainGroup = Thread.currentThread().getThreadGroup();System.out.println("activeCount=" + mainGroup.activeCount());System.out.println("activeGroupCount=" + mainGroup.activeGroupCount());mainGroup.list();System.out.println("--------------------------");System.out.println("parentOf=" + mainGroup.parentOf(group));}
}
  • activeCount() 用于获取group中活跃的线程, 这只是个估计值, 并不能百分之百地保证数字一定正确, 原因前面已经分析过, 该方法会递归获取其他子group中的活跃线程。
  • activeGroupCount() 用于获取group中活跃的子group, 这也是一个近似估值, 该方法也会递归获取所有的子group。
  • getMaxPriority() 用于获取group的优先级,默认情况下,Group的优先级为10,在该group中, 所有线程的优先级都不能大于group的优先级。
  • getName() 用于获取group的名字。
  • getParent() 用于获取group的父group, 如果父group不存在, 则会返回null, 比如system group的父group就为null。
  • list() 该方法没有返回值, 执行该方法会将group中所有的活跃线程信息全部输出到控制台, 也就是System.out。
  • parentOf(Thread Group g) 会判断当前group是不是给定group的父group, 另外如果给定的group就是自己本身,那么该方法也会返回true。
  • setMaxPriority(int pri) 会指定group的最大优先级, 最大优先级不能超过父group的最大优先级, 执行该方法不仅会改变当前group的最大优先级, 还会改变所有子group的最大优先级

4.2 ThreadGroup的interrupt

interrupt一个thread group会导致该group中所有的active线程都被interrupt, 也就是说该group中每一个线程的interrupt标识都被设置了, 下面是Thread Group interrupt方法的源码:
 

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方法案例:


import java.util.concurrent.TimeUnit;public class TestThreadInterrupt {public static void main(String[] args) throws InterruptedException {ThreadGroup group = new ThreadGroup("TestGroup");new Thread(group,() -> {while(true) {try {TimeUnit.MILLISECONDS.sleep(2);} catch (InterruptedException e) {e.printStackTrace();}}},"t1").start();new Thread(group,()-> {try {TimeUnit.MILLISECONDS.sleep(1);} catch (InterruptedException e) {e.printStackTrace();}},"t2").start();TimeUnit.MILLISECONDS.sleep(2);group.interrupt();}
}

4.3ThreadGroup的destroy

destroy用于销毁Thread Group, 该方法只是针对一个没有任何active线程的group进行一次destroy标记, 调用该方法的直接结果是在父group中将自己移除:

public class ThreadGroupDestroy {public static void main(String[] args) {ThreadGroup group = new ThreadGroup("TestGroup");ThreadGroup mainGroup = Thread.currentThread().getThreadGroup();System.out.println("group.isDestroyed=" + group.isDestroyed());mainGroup.list();group.destroy();System.out.println("group.isDestroyed=" + group.isDestroyed());mainGroup.list();}
}

4.4守护ThreadGroup

线程可以设置为守护线程, Thread Group也可以设置为守护Thread Group, 但是若将一个Thread Group设置为daemon, 也并不会影响线程的daemon属性, 如果一个Thread Group的daemon被设置为true, 那么在group中没有任何active线程的时候该group将自动destroy, 下面我们给出一个简单的例子来对其进行说明:


import java.util.concurrent.TimeUnit;public class ThreadGroupDaemon {public static void main(String[] args) throws InterruptedException {ThreadGroup group = new ThreadGroup("Group1");new Thread(group,()-> {try {TimeUnit.SECONDS.sleep(1);} catch (InterruptedException e) {e.printStackTrace();}},"group1-thread1").start();ThreadGroup group2 = new ThreadGroup("Group2");new Thread(group2,()-> {try {TimeUnit.SECONDS.sleep(1);} catch (InterruptedException e) {e.printStackTrace();}},"group2-thread1").start();group2.setDaemon(true);TimeUnit.SECONDS.sleep(3);System.out.println(group.isDestroyed());System.out.println(group2.isDestroyed());}
}

在上面的代码中, 第二个group的daemon被设置为true, 当其中没有active线程的时候, 该group将会自动被destroy, 而第一个group则相反。

相关文章:

第6章 ThreadGroup详细讲解(Java高并发编程详解:多线程与系统设计)

1.ThreadGroup 与 Thread 在Java程序中&#xff0c; 默认情况下&#xff0c; 新的线程都会被加入到main线程所在的group中&#xff0c; main线程的group名字同线程名。如同线程存在父子关系一样&#xff0c; Thread Group同样也存在父子关系。图6-1就很好地说明了父子thread、父…...

CentOS 7乱码问题如何解决?

1.使用超级用户操作: sudo su2.修改i18n配置文件&#xff1a; vi /etc/sysconfig/i18n将文件修改或添加为以下内容&#xff1a; LANG"zh_CN.UTF8" LC_ALL"zh_CN.UTF8"保存并退出&#xff08;按Esc键&#xff0c;输入:wq&#xff0c;然后回车&#xff09…...

JavaScript语言的多线程编程

JavaScript语言的多线程编程 JavaScript是一种广泛使用的编程语言&#xff0c;主要用于网页开发。由于其单线程的特性&#xff0c;JavaScript 一直以来都有“无法进行多线程编程”的印象。尽管如此&#xff0c;随着技术的发展&#xff0c;JavaScript也逐渐引入了多线程的概念&…...

OpenSeaOtter使用手册-变更通知和持续部署

我们在OpenSeaOtter Server 0.1.1版本增加的镜像变更通知功能。通过镜像变更通知和OpenSeaOtter Agent就可以轻松获得持续部署能力。 镜像变更通知是通过push的方式下发到Agent的&#xff0c;Agent所在机器不需要外网地址。在Agent收到镜像变更通知后&#xff0c;就会调用对应的…...

(2)STM32 USB设备开发-USB虚拟串口

例程&#xff1a;STM32USBdevice: 基于STM32的USB设备例子程序 - Gitee.com 本篇为USB虚拟串口教程&#xff0c;没有知识&#xff0c;全是实操&#xff0c;按照步骤就能获得一个STM32的USB虚拟串口。本例子是在野火F103MINI开发板上验证的&#xff0c;如果代码中出现一些外设的…...

他把智能科技引入现代农业领域

江苏田倍丰农业科技有限公司&#xff08;以下简称“田倍丰”&#xff09;是一家专注于粮油种植的农业科技公司&#xff0c;为拥有300亩以上田地的大户提供全面的解决方案。田倍丰通过与当地政府合作&#xff0c;将土地承包给大户&#xff0c;并提供农资和技术&#xff0c;实现利…...

ingress-nginx代理tcp使其能外部访问mysql

一、helm部署mysql主从复制 helm repo add bitnami https://charts.bitnami.com/bitnami helm repo updatehelm pull bitnami/mysql 解压后编辑values.yaml文件&#xff0c;修改如下&#xff08;storageclass已设置默认类&#xff09; 117 ## param architecture MySQL archit…...

麒麟操作系统服务架构保姆级教程(十三)tomcat环境安装以及LNMT架构

如果你想拥有你从未拥有过的东西&#xff0c;那么你必须去做你从未做过的事情 之前咱们学习了LNMP架构&#xff0c;但是PHP对于技术来说确实是老掉牙了&#xff0c;PHP的市场占有量越来越少了&#xff0c;我认识一个10年的PHP开发工程师&#xff0c;十年工资从15k到今天的6k&am…...

亚博microros小车-原生ubuntu支持系列:4-手部检测

一 准备工作 在学习手部检测之前&#xff0c;有2个准备工作。 1 确保小车的摄像头能显示画面 参见&#xff1a;亚博microros小车-原生ubuntu支持系列&#xff1a;2-摄像头控制-CSDN博客 启动图传代理&#xff1a; docker run -it --rm -v /dev:/dev -v /dev/shm:/dev/shm …...

关于回调函数(callback)

简介 在C中&#xff0c;回调函数是一种常见的编程技术&#xff0c;它允许你将一个函数作为参数传递给另一个函数&#xff0c;并在适当的时候调用它。回调函数通常用于事件处理、异步编程和模块化设计中。 1、函数指针&#xff1a;在C中&#xff0c;回调函数通常是通过函数指针…...

Linux Bash 中使用重定向运算符的 5 种方法

注&#xff1a;机翻&#xff0c;未校。 Five ways to use redirect operators in Bash Posted: January 22, 2021 | by Damon Garn Redirect operators are a basic but essential part of working at the Bash command line. See how to safely redirect input and output t…...

硬件作品3----STM32F103RCT6最小系统板MCU配置

参考文章&#xff1a;对stm32F103RCT6原理图解析&#xff08;详细&#xff09;-CSDN博客 本想绘制稍微复杂一些的电路&#xff0c;但是出现很多问题&#xff0c;因此先绘制一块最小系统板进行原理、绘制方法的验证。 设计难度&#xff1a;★ 适合人群&#xff1a;初学者 一、…...

人脸识别打卡系统--基于QT(附源码)

逃离舒适区 项目源代码放在我的仓库中&#xff0c;有需要自取 项目地址 https://gitcode.com/hujiahangdewa/Face_recognition.git 文章目录 一、项目结构分析二、服务器的搭建三、客户端的搭建四、人脸识别库的申请五、基于人脸识别库的识别判断六、QT人脸识别----调用百度ai…...

【深度学习入门】深度学习知识点总结

一、卷积 &#xff08;1&#xff09;什么是卷积 定义&#xff1a;特征图的局部与卷积核做内积的操作。 作用&#xff1a;① 广泛应用于图像处理领域。卷积操作可以提取图片中的特征&#xff0c;低层的卷积层提取局部特征&#xff0c;如&#xff1a;边缘、线条、角。 ② 高层…...

通过视觉语言模型蒸馏进行 3D 形状零件分割

大家读完觉得有帮助记得关注和点赞&#xff01;&#xff01;&#xff01;对应英文要求比较高&#xff0c;特此说明&#xff01; Abstract This paper proposes a cross-modal distillation framework, PartDistill, which transfers 2D knowledge from vision-language models …...

机器学习10-解读CNN代码Pytorch版

机器学习10-解读CNN代码Pytorch版 我个人是Java程序员&#xff0c;关于Python代码的使用过程中的相关代码事项&#xff0c;在此进行记录 文章目录 机器学习10-解读CNN代码Pytorch版1-核心逻辑脉络2-参考网址3-解读CNN代码Pytorch版本1-MNIST数据集读取2-CNN网络的定义1-无注释版…...

微服务学习-Gateway 统一微服务入口

1. 微服务为什么需要 API 网关&#xff1f; 1.1. 在微服务架构中&#xff0c;通常一个系统会被拆分为多个微服务&#xff0c;面对多个微服务客户端应该如何去调用呢&#xff1f; 如果根据每个微服务的地址发起调用&#xff0c;存在如下问题&#xff1a; 客户端多次请求不同的…...

2025寒假备战蓝桥杯02---朴素二分查找升级版本的学习+分别求解左右端点

文章目录 1.朴素二分查找的升级版2.查找左端点3.查找右端点4.代码的编写 1.朴素二分查找的升级版 和之前介绍的这个二分查找相比&#xff0c;我觉得这个区别就是我们的这个二分查找需要找到的是一个区间&#xff0c;而不是这个区间里面的某一个元素的位置&#xff1b; 2.查找…...

PHP语言的软件工程

PHP语言的软件工程 引言 软件工程是计算机科学中的一个重要分支&#xff0c;它涉及软件的规划、开发、测试和维护。在现代开发中&#xff0c;PHP作为一种流行的服务器端脚本语言&#xff0c;广泛应用于网页开发和各种企业应用中。本文将深入探讨PHP语言在软件工程中的应用&am…...

linux-FTP服务配置与应用

也许你对FTP不陌生&#xff0c;但是你是否了解FTP到底是个什么玩意&#xff1f; FTP 是File Transfer Protocol&#xff08;文件传输协议&#xff09;的英文简称&#xff0c;而中文简称为 “文传协议” 用于Internet上的控制文件的双向传输。同时&#xff0c;它也是一个应用程序…...

【大模型RAG】拍照搜题技术架构速览:三层管道、两级检索、兜底大模型

摘要 拍照搜题系统采用“三层管道&#xff08;多模态 OCR → 语义检索 → 答案渲染&#xff09;、两级检索&#xff08;倒排 BM25 向量 HNSW&#xff09;并以大语言模型兜底”的整体框架&#xff1a; 多模态 OCR 层 将题目图片经过超分、去噪、倾斜校正后&#xff0c;分别用…...

模型参数、模型存储精度、参数与显存

模型参数量衡量单位 M&#xff1a;百万&#xff08;Million&#xff09; B&#xff1a;十亿&#xff08;Billion&#xff09; 1 B 1000 M 1B 1000M 1B1000M 参数存储精度 模型参数是固定的&#xff0c;但是一个参数所表示多少字节不一定&#xff0c;需要看这个参数以什么…...

2025年能源电力系统与流体力学国际会议 (EPSFD 2025)

2025年能源电力系统与流体力学国际会议&#xff08;EPSFD 2025&#xff09;将于本年度在美丽的杭州盛大召开。作为全球能源、电力系统以及流体力学领域的顶级盛会&#xff0c;EPSFD 2025旨在为来自世界各地的科学家、工程师和研究人员提供一个展示最新研究成果、分享实践经验及…...

《用户共鸣指数(E)驱动品牌大模型种草:如何抢占大模型搜索结果情感高地》

在注意力分散、内容高度同质化的时代&#xff0c;情感连接已成为品牌破圈的关键通道。我们在服务大量品牌客户的过程中发现&#xff0c;消费者对内容的“有感”程度&#xff0c;正日益成为影响品牌传播效率与转化率的核心变量。在生成式AI驱动的内容生成与推荐环境中&#xff0…...

在四层代理中还原真实客户端ngx_stream_realip_module

一、模块原理与价值 PROXY Protocol 回溯 第三方负载均衡&#xff08;如 HAProxy、AWS NLB、阿里 SLB&#xff09;发起上游连接时&#xff0c;将真实客户端 IP/Port 写入 PROXY Protocol v1/v2 头。Stream 层接收到头部后&#xff0c;ngx_stream_realip_module 从中提取原始信息…...

P3 QT项目----记事本(3.8)

3.8 记事本项目总结 项目源码 1.main.cpp #include "widget.h" #include <QApplication> int main(int argc, char *argv[]) {QApplication a(argc, argv);Widget w;w.show();return a.exec(); } 2.widget.cpp #include "widget.h" #include &q…...

Mac软件卸载指南,简单易懂!

刚和Adobe分手&#xff0c;它却总在Library里给你写"回忆录"&#xff1f;卸载的Final Cut Pro像电子幽灵般阴魂不散&#xff1f;总是会有残留文件&#xff0c;别慌&#xff01;这份Mac软件卸载指南&#xff0c;将用最硬核的方式教你"数字分手术"&#xff0…...

NFT模式:数字资产确权与链游经济系统构建

NFT模式&#xff1a;数字资产确权与链游经济系统构建 ——从技术架构到可持续生态的范式革命 一、确权技术革新&#xff1a;构建可信数字资产基石 1. 区块链底层架构的进化 跨链互操作协议&#xff1a;基于LayerZero协议实现以太坊、Solana等公链资产互通&#xff0c;通过零知…...

【JavaWeb】Docker项目部署

引言 之前学习了Linux操作系统的常见命令&#xff0c;在Linux上安装软件&#xff0c;以及如何在Linux上部署一个单体项目&#xff0c;大多数同学都会有相同的感受&#xff0c;那就是麻烦。 核心体现在三点&#xff1a; 命令太多了&#xff0c;记不住 软件安装包名字复杂&…...

代理篇12|深入理解 Vite中的Proxy接口代理配置

在前端开发中,常常会遇到 跨域请求接口 的情况。为了解决这个问题,Vite 和 Webpack 都提供了 proxy 代理功能,用于将本地开发请求转发到后端服务器。 什么是代理(proxy)? 代理是在开发过程中,前端项目通过开发服务器,将指定的请求“转发”到真实的后端服务器,从而绕…...