第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则相反。

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

力扣刷题—爬楼梯

文章目录 一、题目二、示例三、解析四、代码 一、题目 假设你正在爬楼梯。需要 n 阶你才能到达楼顶。 每次你可以爬 1 或 2 个台阶。你有多少种不同的方法可以爬到楼顶呢&#xff1f; 二、示例 输入&#xff1a; n 2输出&#xff1a; 2三、解析 用f(x)表示爬到第x级台阶的方…

Python(十七)excel指定列自动翻译成英文

前言 本章主要讲述在excel的指定列后面添加一列&#xff0c;并翻译成英文 一、效果图 二、代码 实际需求&#xff1a; # -*- codeing utf-8 -*- # time: 2025/1/16 16:32 # Author : Mikasa # # Aim&#xff1a;自动将客户发的货物清单里的商品名称&#xff0c;翻译成英文…

JavaEE

一.web开发概述 1.服务器 解释1&#xff1a;服务器是一款软件&#xff0c;可以向其他发送请求&#xff0c;服务器会做出一个响应。可以在服务器中部署文件&#xff0c;让其他人访问。 解释2&#xff1a;也可以把运行服务器软件的计算机称为服务器 2.安装服务器 Tomcat官方…

基于海思soc的智能产品开发(高、中、低soc、以及和fpga的搭配)

【 声明&#xff1a;版权所有&#xff0c;欢迎转载&#xff0c;请勿用于商业用途。 联系信箱&#xff1a;feixiaoxing 163.com】 市场上关于图像、音频的soc其实非常多&#xff0c;这里面有高、中、低档&#xff0c;开发方式也不相同。之所以会这样&#xff0c;有价格的因素&am…

w~深度学习~合集5

我自己的原文哦~ https://blog.51cto.com/whaosoft/13083433 #Agile But Safe 足式机器人领域又一次迎来创新&#xff01;CMU 与 ETH Zurich 团队联合研发了一个名为 「敏捷但安全」&#xff08;ABS&#xff0c;Agile But Safe&#xff09;的新框架&#xff0c;为四足机器…

Excel重新踩坑6:工作实战总结之根据筛选条件求平均成绩

一、前言&#xff1a; 这个博客的实战场景&#xff1a;给了一组学生数据&#xff0c;这些数据中&#xff0c;有全市20个社区&#xff0c;1-9年级的学生各科成绩。要求按照各社区统计1-9年级的所有学生各科平均值。下面首先介绍会用到的一些函数&#xff0c;然后再简单说明实战…

STL容器-- list的模拟实现(附源码)

STL容器-- list的模拟实现&#xff08;附源码&#xff09; List的实现主要考察我们对list这一容器的理解&#xff0c;和代码的编写能力&#xff0c;通过上节对list容器的使用&#xff0c;我们对list容器已经有了一些基本的了解&#xff0c;接下来就让我们来实现一些list容器常见…

PageHelper快速使用

依赖 <!--分页插件PageHelper--> <dependency><groupId>com.github.pagehelper</groupId><artifactId>pagehelper-spring-boot-starter</artifactId><version>1.4.7</version> </dependency>示例 /** * 封装分页结果…

大华Java开发面试题及参考答案 (上)

TCP 的三次握手和四次挥手过程中各个状态的细节是怎样的&#xff1f; TCP&#xff08;Transmission Control Protocol&#xff09;是一种面向连接的、可靠的传输层协议&#xff0c;其三次握手和四次挥手过程涉及多个状态&#xff0c;以下是详细的状态细节&#xff1a; 三次握手…

ACL基础理论

ACL ——访问控制列表 ACL属于策略的一种 ACL访问控制列表的作用&#xff1a; 访问控制&#xff1a;在路由器流量流入或流出的接口上&#xff0c;匹配流量&#xff0c;然后执行设定好的动作&#xff1a;permit&#xff08;允许&#xff09;、deny&#xff08;拒绝&#xff…

【陕西省乡镇界】面图层shp格式arcgis数据乡镇名称和编码2020年wgs84坐标无偏移内容测评

标题中的“陕西省乡镇界面图层shp格式arcgis数据乡镇名称和编码2020年wgs84坐标无偏移.zip”表明这是一个地理信息系统&#xff08;GIS&#xff09;的数据集&#xff0c;专为陕西省的乡镇区域设计。该数据集以Shapefile&#xff08;shp&#xff09;格式提供&#xff0c;是GIS领…

简述mysql 主从复制原理及其工作过程,配置一主两从并验证

第一种基于binlog的主从同步 首先对主库进行配置&#xff1a; [rootopenEuler-1 ~]# vim /etc/my.cnf 启动服务 [rootopenEuler-1 ~]# systemctl enable --now mysqld 主库的配置 从库的配置 第一个从库 [rootopenEuler-1 ~]# vim /etc/my.cnf [rootopenEuler-1 ~]# sys…

(一)相机标定——四大坐标系的介绍、对应转换、畸变原理以及OpenCV完整代码实战(C++版)

一、四大坐标系介绍 1&#xff0c;世界坐标系 从这个世界&#xff08;world&#xff09;的视角来看物体 世界坐标系是3D空间坐标&#xff0c;每个点的位置用 ( X w , Y w , Z w ) (X_w,Y_w,Z_w) (Xw​,Yw​,Zw​)表示 2&#xff0c;相机坐标系 相机本身具有一个坐标系&…

Hugging Face功能介绍,及在线体验文生图模型Flux

Hugging Face简介 对于非机器学习或深度学习领域的人士来说&#xff0c;Hugging Face这个名字可能并不耳熟。然而&#xff0c;随着近年来大模型的迅速崛起&#xff0c;相信大家或多或少都有所接触。如果你对这一领域感兴趣&#xff0c;并在GitHub上查阅过一些开源资料&#xf…

华为ENSP:STP和链路聚合的管理与配置

这里将不再过度阐述STP和链路聚合的理论知识&#xff0c;不清楚的同学可以去观看Cisco文章中的理论知识 理论知识https://blog.csdn.net/2301_76341691/article/details/145166547?fromshareblogdetail&sharetypeblogdetail&sharerId145166547&sharereferPC&…

Asp.Net Core 8.0 使用 Serilog 按日志级别写入日志文件的两种方式

1、所需的Nuget包 本文项目的版本是.NET 8.0&#xff0c;如果使用其它版本安装适配版本即可。 Serilog.AspNetCore(8.0.2) Serilog.Sinks.File(5.0.0) Serilog.Expressions(5.0.0) 2、两种配置方式 2.1 代码形式&#xff08;Program.cs&#xff09; 在Program.cs文件中&am…

基于ESP32+VUE+JAVA+Ngnix的一个小型固件编译系统

一、前提 开发ESP32固件时&#xff0c;使用本地环境输出固件时&#xff0c;存在多个开发多种开发平台的问题。会导致最终输出的固件不统一。更可能因为本地的开发环境差异导致固件无法追溯。 基于上述原因&#xff0c;开发了一个小型的固件编译系统。将该系统部署在一台ubutn…

自然语言处理——自注意力机制

一、文字表示方法 在自然语言处理中&#xff0c;如何用数据表示文字是基础问题。独热编码&#xff08;One-hot Encoding &#xff09;是一种简单的方法&#xff0c;例如对于 “我”“你”“他”“猫”“狗” 等字&#xff0c;会将其编码为如 “我 [1 0 0 0 0 ……]”“你 [0 …

[JavaScript] 运算符详解

文章目录 算术运算符&#xff08;Arithmetic Operators&#xff09;注意事项&#xff1a; 比较运算符&#xff08;Comparison Operators&#xff09;注意事项&#xff1a; 逻辑运算符&#xff08;Logical Operators&#xff09;短路运算&#xff1a;逻辑运算符的返回值&#xf…

《汽车维护与修理》是什么级别的期刊?是正规期刊吗?能评职称吗?

​问题解答&#xff1a; 问&#xff1a;《汽车维护与修理》是不是核心期刊&#xff1f; 答&#xff1a;不是&#xff0c;是知网收录的正规学术期刊。 问&#xff1a;《汽车维护与修理》级别&#xff1f; 答&#xff1a;国家级。主管单位&#xff1a;中国汽车维修行业协会 …