Thread的老管家ThreadGroup(二)
大家好我是kconn,我是一个不爱看源码,不喜欢分析源码,更不喜欢写文章的程序员。自从面试被人虐后,我知道我的不足,所以我打算学习Android源码。
最近有感而发想给大家分享下我的鸡汤:“人总是有梦想的,如果不为此拼命努力,怎么会知道梦想是那么遥不可及。”
我麦镇楼!本篇文章是上一篇文章的补充,上篇文章写的有点过于随意,很多细节比较松散,让人看的云里雾里,所以特地抽时间写了这篇文章,让更多的人了解老管家ThreadGroup。
因为是文章的补充,所以源码比较少,没有看过上篇文章的同学可以先去看看了解下。好了,废话不多说,直接进入主题。
ThreadGroup俗称线程组,位于java.lang包下的一个类,是用于统一的线程管理,听名字就知道,相当于是线程的集合。除了系统线程组外,其他的线程组都一定会有父线程组,他们之间是树的关系。
口说无凭,我先上代码给你们肛。
ThreadGroup tomThreadGroup = new ThreadGroup("Tom");
我创建了一个名字为"汤姆"的线程组。那么看下他和他的父线程组。
Log.e("logTag", "tomThreadGroup名:" + leeThreadGroup.getName()); // 输出Tom
Log.e("logTag", "tom的父线程组大名:" + leeThreadGroup.getParent().getName()); // 输出main
接着看下父线程组的父线程组,也就是爷爷线程组。
Log.e("logTag", "Tom的爷爷线程组大名:" + leeThreadGroup.getParent().getParent().getName()); // 输出system
最后看这个“system”的线程组的父线程组,输出的时候崩掉了,错误日志显示空指针。
看过我上篇文章的同学就知道,这个“system”线程组是最强王者,上面没人了,所以父线程组为空。
资料里面说线程组之间名字可重复,那么我们试试看。
ThreadGroup tomThreadGroup = new ThreadGroup("Tom");
ThreadGroup tomThreadGroup2 = new ThreadGroup("Tom");
Log.e("logTag", "tomThreadGroup名:" + leeThreadGroup.getName()); // 输出Tom
Log.e("logTag", "tomThreadGroup2名:" + leeThreadGroup2.getName()); // 输出Tom
写到这里,我很好奇tomThreadGroup和tomThreadGroup2是mainThreadGroup的子线程组,那么mainThreadGroup的子线程组中是否只有他们呢?为了防止混淆,我把tomThreadGroup2的名字改为Jerry。
ThreadGroup tomThreadGroup = new ThreadGroup("Tom");
ThreadGroup jerryThreadGroup = new ThreadGroup("Jerry");
ThreadGroup mainThreadGroup = tomThreadGroup.getParent();
// 定义一个线程组数组,大小为mainThreadGroup活动的线程组数量(包括子线程组合孙子线程组)
ThreadGroup[] threadGroups = new ThreadGroup[mainThreadGroup.activeGroupCount()];
// 复制mainThreadGroup的所有活动子线程组和孙子线程组
mainThreadGroup.enumerate(threadGroups);
// 输出线程组信息
for (ThreadGroup threadGroup : threadGroups) {
Log.e("logTag", threadGroup.getName()); // 两个结果Jerry、Tom
}
既然知道了main那么也可以知道system线程组里面的子线程组。
ThreadGroup systemThreadGroup = mainThreadGroup.getParent();
ThreadGroup[] threadGroups = new ThreadGroup[systemThreadGroup.activeGroupCount()];
systemThreadGroup.enumerate(threadGroups);
for (ThreadGroup threadGroup : threadGroups) {
Log.e("logTag", threadGroup.getName()); // 三个结果main、Jerry、Tom
}
ok,线程组之间的关系了解完了,现在接着看线程组和线程的关系。
Thread tuffyThread = new Thread(jerryThreadGroup, "Tuffy");
Log.e("logTag", "Tuffy所属的线程组:" + tuffyThread.getThreadGroup().getName());// 输出结果是Jerry
然后试下输出Jerry的线程
Thread[] threads = new Thread[jerryThreadGroup.activeCount()];
jerryThreadGroup.enumerate(threads);
for (Thread thread : threads) {
Log.e("logTag", thread.getName()); // 输出结果为空白
}
我蒙蔽了一会,然后醒悟了,我们获取的是Jerry的活动线程,因为Tuffy还没启动,所以他名下没有活动的线程。这情况就像是你去买房,人家还没过户给你,你就先把钱给了人家。
后面start线程之后输出是Tuffy。
这里插个嘴:看过源码的同学也知道,ThreadGroup里面有个list方法,是用来打印线程组的信息,感兴趣的同学可以自己试下。
同理我们可以得出main和system线程组的活动线程。
mian线程组的线程:Binder_2、Binder_1、main(主线程)、Tuffy
system线程组的线程:HeapTaskDaemon、FinalizerWatchdogDaemon、FinalizerDaemon、
ReferenceQueueDaemon、JDWP、Signal Catcher、Binder_2、Binder_1、main、Tuffy
前面说了,线程组之间是树的关系,那么线程组和线程之间就是集合关系,用以下图来表示。
这里插个嘴:资料上面说,如果线程归入某线程组,那么他就不能改投另一个线程组。就像猫和老鼠,Tom猫是一个线程组,Jerry鼠是另一个线程组。现在有一个线程Tuffy,它加入了Jerry组,那么它就无法改投Tom组。另外再说句,如果线程没有表示加入哪个线程组,那么它默认是属于main线程组。
好,接着看ThreadGroup的interrupt方法,资料上面显示这个是中断该线程组中所有线程的方法。实际上它并没有中断。
Thread tuffyThread = new Thread(jerryThreadGroup, new Runnable() {
@Override
public void run() {
for (int i = 0; i < 10; i++) {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
if (i == 3) {
jerryThreadGroup.interrupt();
}
Log.e("logTag", "爆炸虚出:" + i); // 依然我行我素输出到9
}
}
}, "Tuffy");
这里就不太明白,说好的中断呢?在源码中它最后是遍历了它名下的线程,然后调用Thread的interrupt方法(最后实现是native方法)。
百思不得其姐的时候,突然发现这个Thread.sleep抛的异常是InterruptedException,寻思着两者之间会不会有什么骚操作。
Thread tuffyThread = new Thread(jerryThreadGroup, new Runnable() {
@Override
public void run() {
for (int i = 0; i < 10; i++) {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
Log.e("logTag", "异常虚出:" + e.toString());
}
if (i == 3) {
jerryThreadGroup.interrupt();
}
Log.e("logTag", "爆炸虚出:" + i); // 依然我行我素输出到9
}
}
}, "Tuffy");
结果可以看到当虚出到3的时候抛了异常。这里我说下细节,虚出3、java.lang.InterruptedException、4这三个动作是一起完成的,也就是说明此刻的Thread.sleep(1000)是没效的,也就是说interrupt方法中断的是Thread.sleep方法。值得一提的是它只中断一次就是抛异常的那次,后面的5-9依然sleep了1000毫秒。至于为什么,这就留到后面写到Thread文章再详细解释。
好了,最后在结束的之前介绍下Thread和ThreadGroup异常的处理。ThreadGroup实现了Thread.UncaughtExceptionHandler接口,用意在于当线程组中的线程发生异常,那么就会调用Thread.UncaughtExceptionHandler接口的uncaughtException方法。异常的处理我们可以调用线程的setUncaughtExceptionHandler方法进行处理,也可以重写线程组的uncaughtException方法处理。
ThreadGroup jerryThreadGroup = new ThreadGroup("Jerry") {
@Override
public void uncaughtException(Thread t, Throwable e) {
Log.e("logTag", t.getName() + ":" + e.toString()); // Tuffy:java.lang.RuntimeException: 对方不想理你并向你抛出个异常
}
};
Thread tuffyThread = new Thread(jerryThreadGroup, new Runnable() {
@Override
public void run() {
throw new RuntimeException("对方不想理你并向你抛出个异常");
}
},"Tuffy");
肛源码不容易,我也不是大牛,文章也写的不是很好。大家看着有什么想法都可以说,我不一定会看,看了也不一定会回,因为我胃疼!