多线程机制是由java虚拟机支持的,线程是实际计算任务的载体,由java虚拟机执行,用户往往创建新的线程来进行异步处理。java虚拟机需要线程存在,才能真正为用户工作。
为线程服务,用户通过使用线程组的概念批量管理线程,如批量停止或挂起等。
每个线程创建时,都会纳入线程组的树形结构
我们在管理一组对象时往往只是使用数组列表,树形结构的引入解决什么问题,给我们带来什么便利
树形结构如上图,一个线程或线程组创建时,必须要获知它的父线程组,一个线程必须要属于某一个线程组。
即system TheadGroup,需要确保它的唯一性
ThreadGroup.java
static final ThreadGroup systemThreadGroup = new ThreadGroup();
使用静态变量,确保全局只有一个对象
需要先计算出它的父线程组,然后把它加入父线程组。每个父线程组维护一个数组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,…
需要计算其父线程组节点,然后父线程组纳入此节点。每个父线程组通过维护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;
}
子线程节点和子线程组节点的移除操作一致,不同的是操作的列表不同,子线程节点操作的是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节点。
如以下图所示:
巧妙地通过sytem.arraycopy,自身列表对自身列表的复制操作,实现删除。很简洁。
可以说,树形结构为集合操作提供很简洁的形式,就是支持递归遍历。举个例子
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有更加细致的操作选择,满足用户更多的批量处理需求。