Java并发编程与技术内幕:ThreadGroup线程组应用

2024-02-28 10:32

本文主要是介绍Java并发编程与技术内幕:ThreadGroup线程组应用,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

       摘要:线程组ThreadGroup表示一组线程的集合,一旦一个线程归属到一个线程组之中后,就不能再更换其所在的线程组。那么为什么要使用线程组呢?个人认为有以下的好处:方便统一管理,线程组可以进行复制,快速定位到一个线程,统一进行异常设置等。ThreadGroup它其实并不属于Java并发包中的内容,它是java.lang中的内容。但是掌握对其的于理解,在实际应用中有很大的帮助。

一、基本方法

1、获取当前线程组名

 

Thread.currentThread().getThreadGroup().getName()

在main方法是调用输出是:main

 

2、将线程放入到一个线程组中去

 

 
  1. ThreadGroup threadGroup1 = new ThreadGroup("group1");

  2. ThreadGroup threadGroup2 = new ThreadGroup("group2");

  3. Thread thread1 =new Thread(threadGroup1, "group1's member");

  4. Thread thread2 =new Thread(threadGroup2, "group2's member");

其中Thread中和ThreadGroup相关的构造函数:

 

 

 
  1. public Thread(ThreadGroup group, Runnable target) {

  2. init(group, target, "Thread-" + nextThreadNum(), 0);

  3. }

  4.  
  5.  
  6. public Thread(ThreadGroup group, String name) {

  7. init(group, null, name, 0);

  8. }

  9.  
  10.  
  11. public Thread(ThreadGroup group, Runnable target, String name) {

  12. init(group, target, name, 0);

  13. }

  14.  
  15. public Thread(ThreadGroup group, Runnable target, String name,

  16. long stackSize) {

  17. init(group, target, name, stackSize);

  18. }

它们最终都是调用同一个函数:

 

 

 
  1. private void init(ThreadGroup g, Runnable target, String name,

  2. long stackSize) {

  3. Thread parent = currentThread();

  4. SecurityManager security = System.getSecurityManager();

  5. if (g == null) {

  6. //安全检查

  7. if (security != null) {

  8. g = security.getThreadGroup();

  9. }

  10.  
  11. //设置线程组

  12. if (g == null) {

  13. g = parent.getThreadGroup();

  14. }

  15. }

  16.  
  17. //检查可达性

  18. g.checkAccess();

  19.  
  20. //是否有权限访问

  21. if (security != null) {

  22. if (isCCLOverridden(getClass())) {

  23. security.checkPermission(SUBCLASS_IMPLEMENTATION_PERMISSION);

  24. }

  25. }

  26.  
  27. //往线程组添加线程但未启动

  28. g.addUnstarted();

  29.  
  30. this.group = g;

  31. this.daemon = parent.isDaemon();//是否守护线程

  32. this.priority = parent.getPriority();//优先级

  33. this.name = name.toCharArray();

  34. if (security == null || isCCLOverridden(parent.getClass()))

  35. this.contextClassLoader = parent.getContextClassLoader();

  36. else

  37. this.contextClassLoader = parent.contextClassLoader;

  38. this.inheritedAccessControlContext = AccessController.getContext();

  39. this.target = target;

  40. setPriority(priority);

  41. if (parent.inheritableThreadLocals != null)

  42. this.inheritableThreadLocals =

  43. ThreadLocal.createInheritedMap(parent.inheritableThreadLocals);

  44. this.stackSize = stackSize;

  45. tid = nextThreadID();

  46. this.me = this;

  47. }


3、复制线程组:

 

 

 

  1. //这样可以复制group里面的thread信息

  2. Thread[] threads = new Thread[threadGroup.activeCount()];

  3. threadGroup.enumerate(threads);

这里的activeCount很明显就是取得活动的线程,注意。默认情况 下,连同其子线程组也会进行复制。

 

 

 

 

 

4、未捕获异常处理

ThreadGroup中有一个uncaughtException()方法。当线程组中某个线程发生Unchecked exception异常时,由执行环境调用此方法进行相关处理,如果有必要,可以重新定义此方法
 

二、应用实例

1、实例应用

 

 

 
  1. package com.func.axc.threadgroup;

  2.  
  3.  
  4. import java.util.Date;

  5. import java.util.Random;

  6. import java.util.concurrent.TimeUnit;

  7.  
  8. class Result {

  9.  
  10. private String name;

  11.  
  12. public String getName() {

  13. return name;

  14. }

  15.  
  16. public void setName(String name) {

  17. this.name = name;

  18. }

  19.  
  20. }

  21.  
  22. public class SearchTask implements Runnable {

  23.  
  24. public SearchTask(Result result) {

  25. this.result = result;

  26. }

  27.  
  28. private Result result;

  29.  
  30. @Override

  31. public void run() {

  32. String name = Thread.currentThread().getName();

  33. System.out.println("Thread Start " + name);

  34. try {

  35. doTask();

  36. result.setName(name);

  37. } catch (InterruptedException e) {

  38. System.out.printf("Thread %s: Interrupted\n", name);

  39. return;

  40. }

  41. System.out.println("Thread end " + name);

  42. }

  43.  
  44. private void doTask() throws InterruptedException {

  45. Random random = new Random((new Date()).getTime());

  46. int value = (int) (random.nextDouble() * 100);

  47. System.out.printf("Thread %s: %d\n", Thread.currentThread().getName(),

  48. value);

  49. TimeUnit.SECONDS.sleep(value);

  50. }

  51.  
  52. public static void main(String[] args) {

  53. System.out.println("main thread start:");

  54.  
  55. //创建5个线程,并入group里面进行管理

  56. ThreadGroup threadGroup = new ThreadGroup("Searcher");

  57. Result result = new Result();

  58. SearchTask searchTask = new SearchTask(result);

  59. for (int i = 0; i < 5; i++) {

  60. Thread thred = new Thread(threadGroup, searchTask);

  61. thred.start();

  62. try {

  63. TimeUnit.SECONDS.sleep(1);

  64. } catch (InterruptedException e) {

  65. e.printStackTrace();

  66. }

  67. }

  68. //通过这种方法可以看group里面的所有信息

  69. System.out.printf("Number of Threads: %d\n", threadGroup.activeCount());

  70. System.out.printf("Information about the Thread Group\n");

  71. threadGroup.list();

  72.  
  73. //这样可以复制group里面的thread信息

  74. Thread[] threads = new Thread[threadGroup.activeCount()];

  75. threadGroup.enumerate(threads);

  76. for (int i = 0; i < threadGroup.activeCount(); i++) {

  77. System.out.printf("Thread %s: %s\n", threads[i].getName(),

  78. threads[i].getState());

  79. }

  80.  
  81. waitFinish(threadGroup);

  82. //将group里面的所有线程都给interpet

  83. threadGroup.interrupt();

  84.  
  85. System.out.println("main thread end:");

  86. }

  87.  
  88. private static void waitFinish(ThreadGroup threadGroup) {

  89. while (threadGroup.activeCount() > 0) {

  90. try {

  91. TimeUnit.SECONDS.sleep(1);

  92. } catch (InterruptedException e) {

  93. e.printStackTrace();

  94. }

  95. }

  96. }

  97.  
  98. }

输出结果:

 

 

 

 

 

2、统一异常处理实例

 

 
  1. package com.func.axc.threadgroup;

  2.  
  3. /**

  4. * 功能概要:

  5. *

  6. * @author linbingwen

  7. * @since 2016年6月11日

  8. */

  9. public class ThreadGroupDemo {

  10.  
  11. public static void main(String[] args) {

  12. ThreadGroup threadGroup1 =

  13. // 这是匿名类写法

  14. new ThreadGroup("group1") {

  15. // 继承ThreadGroup并重新定义以下方法

  16. // 在线程成员抛出unchecked exception

  17. // 会执行此方法

  18. public void uncaughtException(Thread t, Throwable e) {

  19. System.out.println(t.getName() + ": " + e.getMessage());

  20. }

  21. };

  22. // 这是匿名类写法

  23. Thread thread1 =

  24. // 这个线程是threadGroup1的一员

  25. new Thread(threadGroup1, new Runnable() {

  26. public void run() {

  27. // 抛出unchecked异常

  28. throw new RuntimeException("测试异常");

  29. }

  30. });

  31.  
  32. thread1.start();

  33. }

  34.  
  35. }

 

三、源码解读

 

首先看其包含的变量

 

 
  1. public class ThreadGroup implements Thread.UncaughtExceptionHandler {

  2. private final ThreadGroup parent;//父亲ThreadGroup

  3. String name;//ThreadGroupr 的名称

  4. int maxPriority;//线程最大优先级

  5. boolean destroyed;//是否被销毁

  6. boolean daemon;//是否守护线程

  7. boolean vmAllowSuspension;//是否可以中断

  8.  
  9. int nUnstartedThreads = 0;//还未启动的线程

  10. int nthreads;//ThreadGroup中线程数目

  11. Thread threads[];//ThreadGroup中的线程

  12.  
  13. int ngroups;//线程组数目

  14. ThreadGroup groups[];//线程组数组


从源码中可以看出,其包含的变量并不是很多。这里需要注意

 

(1)线程组也可以包含其他线程组。如上面的groups[].

(2)线程组构成一棵树,在树中,除了初始线程组外,每个线程组都有一个父线程组

构造函数:

 

 
  1. //私有构造函数

  2. private ThreadGroup() {

  3. this.name = "system";

  4. this.maxPriority = Thread.MAX_PRIORITY;

  5. this.parent = null;

  6. }

  7.  
  8. //默认是以当前ThreadGroup传入作为parent ThreadGroup,新线程组的父线程组是目前正在运行线程的线程组。

  9. public ThreadGroup(String name) {

  10. this(Thread.currentThread().getThreadGroup(), name);

  11. }

  12.  
  13. //构造函数

  14. public ThreadGroup(ThreadGroup parent, String name) {

  15. this(checkParentAccess(parent), parent, name);

  16. }

  17.  
  18. //私有构造函数

  19. private ThreadGroup(Void unused, ThreadGroup parent, String name) {

  20. this.name = name;

  21. this.maxPriority = parent.maxPriority;

  22. this.daemon = parent.daemon;

  23. this.vmAllowSuspension = parent.vmAllowSuspension;

  24. this.parent = parent;

  25. parent.add(this);

  26. }

其终的调用构造函数只有一个,父线程组的 checkAccess 方法在checkParentAccess中会调用:

 

 

 
  1. //检查parent ThreadGroup

  2. private static Void checkParentAccess(ThreadGroup parent) {

  3. parent.checkAccess();

  4. return null;

  5. }

未捕获异常设置:

 

 

 
  1. public void uncaughtException(Thread t, Throwable e) {

  2. if (parent != null) {

  3. parent.uncaughtException(t, e);//父线程组不为空,设置到父线程组

  4. } else {

  5. Thread.UncaughtExceptionHandler ueh =

  6. Thread.getDefaultUncaughtExceptionHandler();

  7. if (ueh != null) {

  8. ueh.uncaughtException(t, e);

  9. } else if (!(e instanceof ThreadDeath)) {

  10. System.err.print("Exception in thread \""

  11. + t.getName() + "\" ");

  12. e.printStackTrace(System.err);

  13. }

  14. }

  15. }

如果父线程组存在, 则调用它的uncaughtException方法.
如果父线程组不存在, 但指定了默认处理器 (下节中的As the default handler for the application), 则调用默认的处理器
如果默认处理器没有设置, 则写错误日志.但如果 exception是ThreadDeath实例的话, 忽略

线程组复制:

 

 

 
  1. //此线程组及其子组中的所有活动线程复制到指定数组中。

  2. public int enumerate(ThreadGroup list[]) {

  3. checkAccess();

  4. return enumerate(list, 0, true);

  5. }

  6. //此线程组及其子组中的所有活动线程复制到指定数组中。

  7. public int enumerate(ThreadGroup list[], boolean recurse) {

  8. checkAccess();

  9. return enumerate(list, 0, recurse);

  10. }

  11. //此线程组中的所有活动线程复制到指定数组中。如果 recurse 标志为 true,则还包括对此线程的子组中的所有活动线程的引用。如果数组太小而无法保持所有线程,则 //忽略额外的线程。

  12. private int enumerate(ThreadGroup list[], int n, boolean recurse) {

  13. int ngroupsSnapshot = 0;

  14. ThreadGroup[] groupsSnapshot = null;

  15. synchronized (this) {

  16. if (destroyed) {

  17. return 0;

  18. }

  19. int ng = ngroups;

  20. if (ng > list.length - n) {//防止list放不下线程数目

  21. ng = list.length - n;

  22. }

  23. if (ng > 0) {

  24. System.arraycopy(groups, 0, list, n, ng);//复制线程组

  25. n += ng;

  26. }

  27. if (recurse) { //取得其子组

  28. ngroupsSnapshot = ngroups;

  29. if (groups != null) {

  30. groupsSnapshot = Arrays.copyOf(groups, ngroupsSnapshot);

  31. } else {

  32. groupsSnapshot = null;

  33. }

  34. }

  35. }

  36. if (recurse) {//复制子组

  37. for (int i = 0 ; i < ngroupsSnapshot ; i++) {

  38. n = groupsSnapshot[i].enumerate(list, n, true);

  39. }

  40. }

  41. return n;

  42. }


上面就是一些主要的方法,其它的就不再细讲了。下一讲我们再来看看ThreadFactory、ThreadLocal这些经常看到的类。

这篇关于Java并发编程与技术内幕:ThreadGroup线程组应用的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/755240

相关文章

Java实现检查多个时间段是否有重合

《Java实现检查多个时间段是否有重合》这篇文章主要为大家详细介绍了如何使用Java实现检查多个时间段是否有重合,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起学习一下... 目录流程概述步骤详解China编程步骤1:定义时间段类步骤2:添加时间段步骤3:检查时间段是否有重合步骤4:输出结果示例代码结语作

Java中String字符串使用避坑指南

《Java中String字符串使用避坑指南》Java中的String字符串是我们日常编程中用得最多的类之一,看似简单的String使用,却隐藏着不少“坑”,如果不注意,可能会导致性能问题、意外的错误容... 目录8个避坑点如下:1. 字符串的不可变性:每次修改都创建新对象2. 使用 == 比较字符串,陷阱满

Java判断多个时间段是否重合的方法小结

《Java判断多个时间段是否重合的方法小结》这篇文章主要为大家详细介绍了Java中判断多个时间段是否重合的方法,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起学习一下... 目录判断多个时间段是否有间隔判断时间段集合是否与某时间段重合判断多个时间段是否有间隔实体类内容public class D

IDEA编译报错“java: 常量字符串过长”的原因及解决方法

《IDEA编译报错“java:常量字符串过长”的原因及解决方法》今天在开发过程中,由于尝试将一个文件的Base64字符串设置为常量,结果导致IDEA编译的时候出现了如下报错java:常量字符串过长,... 目录一、问题描述二、问题原因2.1 理论角度2.2 源码角度三、解决方案解决方案①:StringBui

Java覆盖第三方jar包中的某一个类的实现方法

《Java覆盖第三方jar包中的某一个类的实现方法》在我们日常的开发中,经常需要使用第三方的jar包,有时候我们会发现第三方的jar包中的某一个类有问题,或者我们需要定制化修改其中的逻辑,那么应该如何... 目录一、需求描述二、示例描述三、操作步骤四、验证结果五、实现原理一、需求描述需求描述如下:需要在

Java中ArrayList和LinkedList有什么区别举例详解

《Java中ArrayList和LinkedList有什么区别举例详解》:本文主要介绍Java中ArrayList和LinkedList区别的相关资料,包括数据结构特性、核心操作性能、内存与GC影... 目录一、底层数据结构二、核心操作性能对比三、内存与 GC 影响四、扩容机制五、线程安全与并发方案六、工程

JavaScript中的reduce方法执行过程、使用场景及进阶用法

《JavaScript中的reduce方法执行过程、使用场景及进阶用法》:本文主要介绍JavaScript中的reduce方法执行过程、使用场景及进阶用法的相关资料,reduce是JavaScri... 目录1. 什么是reduce2. reduce语法2.1 语法2.2 参数说明3. reduce执行过程

如何使用Java实现请求deepseek

《如何使用Java实现请求deepseek》这篇文章主要为大家详细介绍了如何使用Java实现请求deepseek功能,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起学习一下... 目录1.deepseek的api创建2.Java实现请求deepseek2.1 pom文件2.2 json转化文件2.2

Java调用DeepSeek API的最佳实践及详细代码示例

《Java调用DeepSeekAPI的最佳实践及详细代码示例》:本文主要介绍如何使用Java调用DeepSeekAPI,包括获取API密钥、添加HTTP客户端依赖、创建HTTP请求、处理响应、... 目录1. 获取API密钥2. 添加HTTP客户端依赖3. 创建HTTP请求4. 处理响应5. 错误处理6.

Spring AI集成DeepSeek的详细步骤

《SpringAI集成DeepSeek的详细步骤》DeepSeek作为一款卓越的国产AI模型,越来越多的公司考虑在自己的应用中集成,对于Java应用来说,我们可以借助SpringAI集成DeepSe... 目录DeepSeek 介绍Spring AI 是什么?1、环境准备2、构建项目2.1、pom依赖2.2