线程组(ThreadGroup)源码学习总结

本文深入探讨了Java中的ThreadGroup,解释了它作为线程管理集合的作用,以及如何通过线程组进行统一的异常捕获。文章详细介绍了ThreadGroup的构造函数、线程组的构造方法,特别是线程组如何处理未捕获异常,并提供了示例代码展示其工作原理。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

学习于林炳文Evankaka原创作品。出处http://blog.youkuaiyun.com/evankaka

 

 

ThreadGroup是做什么的

 线程组,顾名思义,像数组一样,将多个线程放在同一个集合中进行管理。另外还有个好处便是可以准确的定位到某个线程和进行统一的异常捕获。

 简单运用

 

//当前所运行线程所属的线程组
Thread.currentThread().getThreadGroup().getName();

//创建新的线程的时候可以指定线程组
ThreadGroup threadGroup1 = new ThreadGroup("group1");//参数为线程组名
ThreadGroup threadGroup2 = new ThreadGroup("group2");
Thread thread1 =new Thread(threadGroup1, "group1's member");
Thread thread2 =new Thread(threadGroup2, "group2's member");

Thread 构造函数

    public Thread(ThreadGroup group, Runnable target) {
	init(group, target, "Thread-" + nextThreadNum(), 0);
    }
 
 
    public Thread(ThreadGroup group, String name) {
	init(group, null, name, 0);
    }
 
 
    public Thread(ThreadGroup group, Runnable target, String name) {
	init(group, target, name, 0);
    }
 
    public Thread(ThreadGroup group, Runnable target, String name,
                  long stackSize) {
	init(group, target, name, stackSize);
    }

四个构造函数的重载

最终都是init(ThreadGroup g, Runnable target, String name,long stackSize)

其中g是所属线程组,target是runable目标,name 名字,stackSize 初始栈大小(0表示默认) [Sparc: 512, Solaris Intel: 256, Sparc 64bit: 1024 all others 0]

private void init(ThreadGroup g, Runnable target, String name,
                      long stackSize) {
	Thread parent = currentThread();
	SecurityManager security = System.getSecurityManager();
	if (g == null) {
         //安全检查
	    if (security != null) {
		g = security.getThreadGroup();
	    }
 
        //设置线程组
	    if (g == null) {
		g = parent.getThreadGroup();
	    }
	}
 
    //检查可达性
	g.checkAccess();
 
     //是否有权限访问
	if (security != null) {
	    if (isCCLOverridden(getClass())) {
	        security.checkPermission(SUBCLASS_IMPLEMENTATION_PERMISSION);
	    }
	}
 
     //往线程组添加线程但未启动
     g.addUnstarted();
 
	this.group = g;
	this.daemon = parent.isDaemon();//是否守护线程
	this.priority = parent.getPriority();//优先级
	this.name = name.toCharArray();
	if (security == null || isCCLOverridden(parent.getClass()))
	    this.contextClassLoader = parent.getContextClassLoader();
	else
	    this.contextClassLoader = parent.contextClassLoader;
	this.inheritedAccessControlContext = AccessController.getContext();
	this.target = target;
	setPriority(priority);
        if (parent.inheritableThreadLocals != null)
	    this.inheritableThreadLocals =
		ThreadLocal.createInheritedMap(parent.inheritableThreadLocals);
        this.stackSize = stackSize;
        tid = nextThreadID();
        this.me = this;
    }
  1. 首先判断是否传入了线程组,若无则用当前创建该新线程的线程(该线程为新线程的父线程)的线程组,若无法用SecurityManager获得便用父线程所属的线程组
  2. 新线程的是否为守护线程,优先级, InheritableThreadLocal,上下文类加载器都是继承的父线程的

下面是InheritableThreadLocal的介绍,是帮助父子线程传递ThreadLocal的类

在创建InheritableThreadLocal对象的时候赋值给线程的t.inheritableThreadLocals变量
在创建新线程的时候会check父线程中t.inheritableThreadLocals变量是否为null,如果不为null则copy一份ThradLocalMap到子线程的t.inheritableThreadLocals成员变量中去
因为复写了getMap(Thread)和CreateMap()方法,所以get值得时候,就可以在getMap(t)的时候就会从t.inheritableThreadLocals中拿到map对象,从而实现了可以拿到父线程ThreadLocal中的值
--------------------- 
作者:骚年编程去 
来源:优快云 
原文:https://blog.youkuaiyun.com/a837199685/article/details/52712547 
版权声明:本文为博主原创文章,转载请附上博文链接!

线程组的构造方法

基本的变量

public class ThreadGroup implements Thread.UncaughtExceptionHandler {
    private final ThreadGroup parent;//父亲ThreadGroup
    String name;//ThreadGroupr 的名称
    int maxPriority;//线程最大优先级
    boolean destroyed;//是否被销毁
    boolean daemon;//是否守护线程
    boolean vmAllowSuspension;//是否可以中断
 
    int nUnstartedThreads = 0;//还未启动的线程
    int nthreads;//ThreadGroup中线程数目
    Thread threads[];//ThreadGroup中的线程
 
    int ngroups;//线程组数目
    ThreadGroup groups[];//线程组数组

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

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

构造函数

//私有构造函数
    private ThreadGroup() { 
    this.name = "system";
    this.maxPriority = Thread.MAX_PRIORITY;
        this.parent = null;
    }
 
    //默认是以当前ThreadGroup传入作为parent  ThreadGroup,新线程组的父线程组是目前正在运行线程的线程组。
    public ThreadGroup(String name) {
    this(Thread.currentThread().getThreadGroup(), name);
    }
 
    //构造函数
    public ThreadGroup(ThreadGroup parent, String name) {
        this(checkParentAccess(parent), parent, name);
    }
 
    //私有构造函数 也是最终使用到的
    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);
    }
--------------------- 
    //检查parent ThreadGroup
    private static Void checkParentAccess(ThreadGroup parent) {
        parent.checkAccess();
        return null;
    }

 

未捕获异常处理

线程组有一个方法,既当线程组里面的某个线程运行时出现了Unchecked exception时(checked和Unchecked异常可以百度一下),线程不会进行报错,而是调用所属线程组的uncaughtException方法,这样便实现了统一未捕获异常的处理

例子

public class ThreadGroupDemo {
 
	public static void main(String[] args) {
		ThreadGroup threadGroup1 =
		// 这是匿名类写法
		new ThreadGroup("group1") {
			// 继承ThreadGroup并重新定义以下方法
			// 在线程成员抛出unchecked exception
			// 会执行此方法
			public void uncaughtException(Thread t, Throwable e) {
				System.out.println(t.getName() + ": " + e.getMessage());
			}
		};
		// 这是匿名类写法
		Thread thread1 =
		// 这个线程是threadGroup1的一员
		new Thread(threadGroup1, new Runnable() {
			public void run() {
				// 抛出unchecked异常
				throw new RuntimeException("测试异常");
			}
		});
 
		thread1.start();
	}
 
}

运行后的结果

Thread-0: 测试异常

 

如果是普通的Thread则会报错

class MAIN7 extends Thread{
    public static void main(String[] args) {
        MAIN7 s=new MAIN7();
        s.start();
    }

    @Override
    public void run() {
        throw new RuntimeException("ceshi");
    }
}

运行:

Exception in thread "Thread-0" java.lang.RuntimeException: ceshi
    at conCurrent.MAIN7.run(ThreadGroupDemo.java:43)

关于未捕获异常的设置

public void uncaughtException(Thread t, Throwable e) {
    if (parent != null) {
        parent.uncaughtException(t, e);//父线程组不为空,设置到父线程组
    } else {
            Thread.UncaughtExceptionHandler ueh = 
                Thread.getDefaultUncaughtExceptionHandler();
            if (ueh != null) {
                ueh.uncaughtException(t, e);
            } else if (!(e instanceof ThreadDeath)) {
        System.err.print("Exception in thread \""
                 + t.getName() + "\" ");
                e.printStackTrace(System.err);
            }
        }
    }

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

线程组的复制

//此线程组及其子组中的所有活动线程复制到指定数组中。
    public int enumerate(ThreadGroup list[]) {
        checkAccess();
    return enumerate(list, 0, true);
    }
     //此线程组及其子组中的所有活动线程复制到指定数组中。
    public int enumerate(ThreadGroup list[], boolean recurse) {
        checkAccess();
    return enumerate(list, 0, recurse);
    }
    //此线程组中的所有活动线程复制到指定数组中。如果 recurse 标志为 true,则还包括对此线程的子组中的所有活动线程的引用。如果数组太小而无法保持所有线程,则    //忽略额外的线程。
    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) {//防止list放不下线程数目
        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;
    }

主要是最后一个的enumerate(ThreadGroup list[], int n, boolean recurse)方法

作用是将该线程组(包括其子线程组中的)的线程全部复制到list线程组中

 

评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值