在之前的分享中提到过关于Thread的部分源码分析,其中提到的一个比较关键的概念就是关于ThreadGroup的概念,也就是说每个一个线程创建之后都会属于一个线程组。每个线程组也可以有自己的父线程组,也可以有自己的子线程组。就想每个线程既有自己的子线程也有自己的父线程。这次分享就是主要来探讨一下关于线程组TreadGroup

ThreadGroup

  在之前的时候我们分析过,对于任意一个创建的新的线程都会被加入到main线程所在的线程组中,而对于main线程来说,是有一个与线程同样名字的线程组。也就是说线程和线程组有包含的关系,而线程组之间有父子关系,线程与线程之间也有父子关系。

创建ThreadGroup

  首先看一下关于ThreadGroup的源码,提供了一个无参构造函数和三个有参构造函数

无参构造
设置了三个属性,线程组名称、最大优先级以及父线程组

  private ThreadGroup() {     // called from C codethis.name = "system";this.maxPriority = Thread.MAX_PRIORITY;this.parent = null;}

有参构造

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);
}

  创建ThreadGroup的方法很简单,就是调用几个构造函数。通过上面的代码分析来看,创建ThreadGroup的时候必须为ThreadGroup添加一个name属性值。或者指定一个父线程组,如果没有指定的话默认为主线程所在线程组。

public class ThreadGroupTest {public static void main(String[] args) {//获取当前线程的线程组ThreadGroup main = Thread.currentThread().getThreadGroup();ThreadGroup group = new ThreadGroup("ThreadGroup-1");System.out.println(group.getParent()==main);ThreadGroup group1 = new ThreadGroup(group,"ThreadGroup-2");System.out.println(group1.getParent()==group);}
}

从上面代码中可以看到首先获取到了当前线程所在的线程组,然后创建了一个ThreadGroup-1的线程组,并没有指定对应的线程组,然后又对创建的线程与当前线程的线程组进行判断结果为True,也就是说明如果不指定线程组默认是以当前线程的线程组作为新建线程组的父线程组。如果指定对应的线程组则为指定的线程组。

Thread数组与ThreadGroup数组的基本操作

Thread数组
  查看ThreadGroup的源码,我们会发现有如下的方法,这个方法的主要的作用是将ThreadGroup中的所有处于active的线程都添加到一个Thread数组中,其中第二个参数表示是否采用递归与非递归的方式。

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++) {//判断是或否处于Active状态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;}

测试代码,首先创建一个测试的ThreadGroup,将这个线程组加入到主线程组中,将创建的线程加入到测试TreadGroup中,调用递归和非递归的方法。

public class TestThreadEnumerate {public static void main(String[] args) throws InterruptedException {//创建一个ThreadGroupThreadGroup testGroup = new ThreadGroup("TestGroup");Thread thread = new Thread(testGroup,()->{while (true){try {TimeUnit.SECONDS.sleep(1);} catch (InterruptedException e) {e.printStackTrace();}}},"TestThread");thread.start();TimeUnit.MILLISECONDS.sleep(2);ThreadGroup mainGroup = Thread.currentThread().getThreadGroup();Thread[] threads = new Thread[mainGroup.activeCount()];int recurseSize = mainGroup.enumerate(threads);System.out.println(recurseSize);recurseSize = mainGroup.enumerate(threads,false);System.out.println(recurseSize);}
}

上面代码的执行结果如下,会发现第二个输出要比第一个输出少1个,这个是因为在递归过程中在testGroup中的线程将不会被包含

注意

  1. 通过上面的enumerate方法获取到的只是一个估计值,也就是说不能保证当前的线程组总的线程都是active的。
  2. 但是通过enumerate方法返回的int值要比Thread数组的长度更有效,因为这个返回值表示的是active的线程数,并不是整个数组的长度。

ThreadGroup数组
  与上面Thread一样也是有一个enumerate方法只不过这个方法的参数传入的是一个ThreadGroup的数组。

private int enumerate(ThreadGroup list[], int n, boolean recurse) {int ngroupsSnapshot = 0;ThreadGroup[] groupsSnapshot = null;synchronized (this) {if (destroyed) {return 0;}int ng = ngroups;if (ng > list.length - n) {ng = list.length - n;}if (ng > 0) {System.arraycopy(groups, 0, list, n, ng);n += ng;}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;}

可以使用同样的思路编写代码测试这个方法对于ThreadGroup的使用

public class TestThreadGroupEnumerate {public static void main(String[] args) throws InterruptedException {ThreadGroup group1 = new ThreadGroup("TestGroup1");ThreadGroup group2 = new ThreadGroup(group1,"TestGroup1");TimeUnit.MILLISECONDS.sleep(2);ThreadGroup mainGroup = Thread.currentThread().getThreadGroup();ThreadGroup[] list = new ThreadGroup[mainGroup.activeGroupCount()];int recureseSize = mainGroup.enumerate(list);System.out.println(recureseSize);recureseSize = mainGroup.enumerate(list,false);System.out.println(recureseSize);}
}

ThreadGroup基本操作

  对于ThreadGroup源码的查看会发现它并不是只对线程进行管理,更主要的功能是对线程进行有计划的组织。

int activeCount()
返回此线程组中活动线程的估计数。
int activeGroupCount()
返回此线程组中活动线程组的估计数。
int getMaxPriority()
返回此线程组的最高优先级。
String getName()
返回此线程组的名称。
ThreadGroup getParent()
返回此线程组的父线程组。
void interrupt()
中断此线程组中的所有线程。
boolean isDaemon()
测试此线程组是否为一个后台程序线程组
boolean isDestroyed()
测试此线程组是否已经被销毁。
void list()
将有关此线程组的信息打印到标准输出。
boolean parentOf(ThreadGroup g)
测试此线程组是否为线程组参数或其祖先线程组之一。

public class TestThreadOtherMethod {public static void main(String[] args) throws InterruptedException {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();TimeUnit.MILLISECONDS.sleep(1);ThreadGroup mainGroup = Thread.currentThread().getThreadGroup();System.out.println("activeCount="+mainGroup.activeCount());System.out.println("activeGroupCount="+mainGroup.activeGroupCount());System.out.println("getMaxPriority="+mainGroup.getMaxPriority());System.out.println("getName="+mainGroup.getName());System.out.println("getParent="+mainGroup.getParent());mainGroup.list();System.out.println("parentOf="+mainGroup.parentOf(group));System.out.println("parentof="+mainGroup.parentOf(mainGroup));}
}


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

  分析源码会看到在这个方法内部调用的是thread的interrupt方法,并且会递归获取子线程组。中断所有的线程。

destroy()
  这个方法用于销毁ThreadGroup线程组,这个方法只针对没有任何active线程的线程组。

public final void destroy() {int ngroupsSnapshot;ThreadGroup[] groupsSnapshot;synchronized (this) {checkAccess();if (destroyed || (nthreads > 0)) {throw new IllegalThreadStateException();}ngroupsSnapshot = ngroups;if (groups != null) {groupsSnapshot = Arrays.copyOf(groups, ngroupsSnapshot);} else {groupsSnapshot = null;}if (parent != null) {destroyed = true;ngroups = 0;groups = null;nthreads = 0;threads = null;}}for (int i = 0 ; i < ngroupsSnapshot ; i += 1) {groupsSnapshot[i].destroy();}if (parent != null) {parent.remove(this);}}

实例代码

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.isDestroyed();System.out.println("group.isDestoryed="+group.isDestroyed());mainGroup.list();}
}


setDaemon()
  将线程组设置为守护线程组,这个属性被设置为true的时候,当线程组中没有active线程的时候线程组会自动destroy。

public class ThreadGroupDaemon {public static void main(String[] args) throws InterruptedException {ThreadGroup group1 = new ThreadGroup("TestGroup1");new Thread(group1,()->{try {TimeUnit.SECONDS.sleep(1);} catch (InterruptedException e) {e.printStackTrace();}},"test_thread1").start();ThreadGroup group2 = new ThreadGroup("TestGroup2");new Thread(group2,()->{try {TimeUnit.SECONDS.sleep(1);} catch (InterruptedException e) {e.printStackTrace();}},"test_thread2").start();group2.setDaemon(true);TimeUnit.SECONDS.sleep(3);System.out.println(group1.isDestroyed());System.out.println(group2.isDestroyed());}
}

总结

  在这篇文章中主要介绍了ThreadGroup与Thread的关系,对ThreadGroup的API做了简单的分析以及简单的介绍。

Java高并发编程详解系列-ThreadGroup介绍相关推荐

  1. Java高并发编程详解系列-Java线程入门

    根据自己学的知识加上从各个网站上收集的资料分享一下关于java高并发编程的知识点.对于代码示例会以Maven工程的形式分享到个人的GitHub上面.   首先介绍一下这个系列的东西是什么,这个系列自己 ...

  2. Java高并发编程详解系列-7种单例模式

    引言 在之前的文章中从技术以及源代码的层面上分析了关于Java高并发的解决方式.这篇博客主要介绍关于单例设计模式.关于单例设计模式大家应该不会陌生,作为GoF23中设计模式中最为基础的设计模式,实现起 ...

  3. Java高并发编程详解系列-线程上下文设计模式及ThreadLocal详解

    导语   在之前的分享中提到过一个概念就是线程之间的通信,都知道在线程之间的通信是一件很消耗资源的事情.但是又不得不去做的一件事情.为了保证多线程线程安全就必须进行线程之间的通信,保证每个线程获取到的 ...

  4. Java高并发编程详解系列-Future设计模式

    导语   假设,在一个使用场景中有一个任务需要执行比较长的时间,通常需要等待任务执行结束之后或者是中途出错之后才能返回结果.在这个期间调用者只能等待,对于这个结果Future设计模式提供了一种凭据式的 ...

  5. Java高并发编程详解系列-类加载

    之前在写关于JVM的时候提到过类加载机制,类加载机制也是在Java面试中被经常问道的一个问题,在这篇博客中就来了解一下关于类加载的知识. 类加载   在JVM执行Java程序的时候实际上执行的编译好的 ...

  6. Java高并发编程详解系列-线程安全数据同步

    在多线程中最为复杂和最为重要的就是线程安全.多个线程访问同一个对象的时候会导致线程安全问题.通过加锁可以避免这种问题.但是在串行执行的过程中又不用考虑线程安全问题,而使用串行程序效率低没有办法将CPU ...

  7. Java高并发编程详解系列-ThreadAPI简单说明

    之前的两篇分享中,简单的从概念上和简单的操作上了解了Thread,这篇分享中主要是看看对于Thread的所有API的使用方式,通过解析源码的方式来了解关于Thread的细节的使用方式 引言   首先在 ...

  8. Java高并发编程详解系列-不可变对象设计模式

    导语   在开发中涉及到的所有关于多线程的问题都离不开共享资源的存在.那么什么是共享资源,共享资源就是被多个线程共同访问的数据资源,而且每个线程都会引起它的变化.伴随共享资源而生的新问题就是线程安全, ...

  9. Java高并发编程详解系列-线程上下文类加载

    前面的分享中提到的最多的概念就是关于类加载器的概念,但是当我们查看Thread源码的时候会发现如下的两个方法,这两个方法就是获取或者设置线程的上下文类加载器的方法,那么为什么要设置这两个方法呢?这个就 ...

最新文章

  1. JUnit 5符合AssertJ
  2. 所有OSGi套件的通用CXF请求拦截器
  3. 【POJ - 2195】Going Home(二分图最优匹配,费用流 或 KM)
  4. 无人机内嵌计算机Manifold 2可部署容器化应用
  5. 矩池云上出现Could not find conda environment
  6. fastunfolding算法_社区发现算法综述—part1
  7. android下拉刷新完全解析,教你如何一分钟实现下拉刷新功能,高仿京东下拉刷新,轻松上手!...
  8. 【10-26】java调试技术学习笔记
  9. acm怎么提交java程序_acm提交java
  10. windowsGHO镜像系统winXPwin7win8win10下载
  11. Android 音频系统:从 AudioTrack 到 AudioFlinger
  12. pageadmin CMS网站制作教程:如何修改栏目类型
  13. CocosCreator微信小游戏接入微信登录获取微信名、头像、经纬度等信息
  14. 虚拟串口工具VSPD的使用
  15. 使用百度地图api制作地图名片
  16. 百度地图api周边搜索功能
  17. Infor 15亿美元融资,CEO终于回应了
  18. 微信公众号获取用户手机
  19. 脱机外挂的命令大揭秘
  20. 汉仪尚巍手书_汉仪尚巍手书字体下载|

热门文章

  1. tomcat顶层架构
  2. PaperWeekly 第十一期
  3. contentProvider 内容提供者
  4. Oracle 10.2.0.1.0升级到 10.2.0.4.0
  5. 路由器上IPSEC ×××
  6. Windows XP 下的 MySQL+Apache+PHP 运行环境架设 (顶)
  7. Python 基础学习 4 ——字典
  8. 在 C++Builder 工程里调用 DLL 函数
  9. python线程安全队列用法
  10. MFC中的CString类使用方法指南