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");

肛源码不容易,我也不是大牛,文章也写的不是很好。大家看着有什么想法都可以说,我不一定会看,看了也不一定会回,因为我胃疼!


bye~

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 199,830评论 5 468
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 83,992评论 2 376
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 146,875评论 0 331
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 53,837评论 1 271
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 62,734评论 5 360
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,091评论 1 277
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,550评论 3 390
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,217评论 0 254
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,368评论 1 294
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,298评论 2 317
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,350评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,027评论 3 315
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,623评论 3 303
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,706评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,940评论 1 255
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,349评论 2 346
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 41,936评论 2 341

推荐阅读更多精彩内容

  • 进程和线程 进程 所有运行中的任务通常对应一个进程,当一个程序进入内存运行时,即变成一个进程.进程是处于运行过程中...
    胜浩_ae28阅读 5,084评论 0 23
  • 进程和线程 进程 所有运行中的任务通常对应一个进程,当一个程序进入内存运行时,即变成一个进程.进程是处于运行过程中...
    小徐andorid阅读 2,796评论 3 53
  • 一. 前言   Thread类作为线程中最基础的类,本篇文章我们就来了解下该类的使用。 二、. Thread类 1...
    骑着乌龟去看海阅读 330评论 0 0
  • 禾谷, 这几天我都只能在晚上你已入睡了才有时间给你写情书,虽然连续五天高强度的学习我也感觉很累,但我仍愿意花一点时...
    草马束木阅读 167评论 0 0
  • userAgent 属性是一个只读的字符串,声明了浏览器用于 HTTP 请求的用户代理头的值。 function ...
    小小肉松阅读 475评论 0 0