Thread的老管家ThreadGroup(二)

作者: k_conn | 来源:发表于2018-10-12 17:38 被阅读5次

    大家好我是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");
    

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


    相关文章

      网友评论

        本文标题:Thread的老管家ThreadGroup(二)

        本文链接:https://www.haomeiwen.com/subject/rsbgaftx.html