ThreadGroup

ThreadGroup树形结构解析

线程Thread和线程组ThreadGroup的关系

1.线程Thread

多线程机制是由java虚拟机支持的,线程是实际计算任务的载体,由java虚拟机执行,用户往往创建新的线程来进行异步处理。java虚拟机需要线程存在,才能真正为用户工作。

2.线程组ThreadGroup

为线程服务,用户通过使用线程组的概念批量管理线程,如批量停止或挂起等。

3.线程和线程组的关系

每个线程创建时,都会纳入线程组的树形结构

4.关于线程组的思考

我们在管理一组对象时往往只是使用数组列表,树形结构的引入解决什么问题,给我们带来什么便利

线程组树形结构ThreadGroup_第1张图片

树形结构如上图,一个线程或线程组创建时,必须要获知它的父线程组,一个线程必须要属于某一个线程组。

树形结构的代码实现

1.根节点唯一性

即system TheadGroup,需要确保它的唯一性

ThreadGroup.java


static final ThreadGroup systemThreadGroup = new ThreadGroup();

使用静态变量,确保全局只有一个对象

2.添加线程节点Thread

需要先计算出它的父线程组,然后把它加入父线程组。每个父线程组维护一个数组Threads[],来保存它的子线程。
计算父线程组有两种方式:
指定父线程组

Thread.java

public Thread(ThreadGroup group, Runnable target) {
    init(group, target, "Thread-" + nextThreadNum(), 0);
}

或者使用缺省父线程组 parent.getThreadGroup()

Thread.java

public Thread(Runnable target) {
    init(null, target, "Thread-" + nextThreadNum(), 0);
}

private void init(ThreadGroup g, Runnable target, String name, long stackSize) {
    Thread parent = currentThread();
    if (g == null) {
        g = parent.getThreadGroup();
    }
    //新线程继承父线程组的优先级
    this.target = target;
    this.priority = parent.getPriority();
    this.daemon = parent.isDaemon();
     ...
}

线程节点的增加

ThreadGroup.java

Thread threads[];

void add(Thread t) {
	//自动扩展
	if (threads == null) {
	     threads = new Thread[4];
	 } else if (nthreads == threads.length) {
	     threads = Arrays.copyOf(threads, nthreads * 2);
	 }
	 //添加节点到threads[]
	 threads[nthreads] = t;
	 ...
 }

threads[]数组实现了自动扩展,因为它无法确定子线程节点有多少。
threads[]的初始长度是4,如果满了,就以2倍速度扩展,8,16,…

3.添加线程组节点ThreadGroup

需要计算其父线程组节点,然后父线程组纳入此节点。每个父线程组通过维护groups[],来保存它的子线程组节点。

ThreadGroup.java

ThreadGroup groups[];

private final void add(ThreadGroup g){

	//实现groups数组的自动扩展
	if (groups == null) {
	    groups = new ThreadGroup[4];
	} else if (ngroups == groups.length) {
	    groups = Arrays.copyOf(groups, ngroups * 2);
	}
	
	//添加节点到groups[]
	groups[ngroups] = g;
}

4.移除子线程节点或子线程组节点

子线程节点和子线程组节点的移除操作一致,不同的是操作的列表不同,子线程节点操作的是threads[],而子线程组节点操作的是groups[]。这里只以子线程节点为例。

ThreadGroup.java

private void remove(Thread t) {
        synchronized (this) {
            if (destroyed) {
                return;
            }
            for (int i = 0 ; i < nthreads ; i++) {
                if (threads[i] == t) {
                    System.arraycopy(threads, i + 1, threads, i, --nthreads - i);
                    // Zap dangling reference to the dead thread so that
                    // the garbage collector will collect it.
                    threads[nthreads] = null;
                    break;
                }
            }
        }
    }

首先要先理解System.arraycopy函数的基本用法

System.java

public static native void arraycopy(Object src,  int  srcPos,
                                        Object dest, int destPos,
                                        int length);

其中,src表示源数组,srcPos表示源数组要复制的起始位置,desc表示目标数组,length表示要复制的长度。
具体可以参考以下博客:
System.arraycopy()方法详解
可以看到,arraycopy是一种浅复制,但此处却利用它实现了数组的删除。举个例子,已知thread[]有A,B,C,D子线程节点,现在要删除B节点。
如以下图所示:
ThreadGroup_第2张图片
巧妙地通过sytem.arraycopy,自身列表对自身列表的复制操作,实现删除。很简洁。

5.集合操作时的递归遍历

可以说,树形结构为集合操作提供很简洁的形式,就是支持递归遍历。举个例子

ThreadGroup.java

public final void interrupt() {
        int ngroupsSnapshot;
        ThreadGroup[] groupsSnapshot;
        synchronized (this) {
            checkAccess();
            //thread遍历
            for (int i = 0 ; i < nthreads ; i++) {
                threads[i].interrupt();
            }
            ngroupsSnapshot = ngroups;
            if (groups != null) {
                groupsSnapshot = Arrays.copyOf(groups, ngroupsSnapshot);
            } else {
                groupsSnapshot = null;
            }
        }
        //threadGroup递归遍历
        for (int i = 0 ; i < ngroupsSnapshot ; i++) {
            groupsSnapshot[i].interrupt();
        }
    }

第一部分,是对thread子节点直接使用for循环进行遍历。
第二部分,对threadGroup子节点调用本函数,递归遍历。

小结

threadGroup通过把各个thread组织成树形结构,新的thread创建时会默认继承父线程组的优先级,对于批量处理thread有更加细致的操作选择,满足用户更多的批量处理需求。

你可能感兴趣的:(ThreadGroup)