java源码解析之Thread(二)

    题接上文。

    到这里,我们已经将thread类主要的内容看了。  回忆一下,可以知道我们好似一直都用到一个线程组的概念:ThreadGroup。 那就去简单看一下吧。

《java源码解析之Thread(二)》

    一看官方权威说明,叼炸天啊哈哈。之前的很多认为比较重要的翻译都写在了图上。 这里为了加深记忆,就在敲一遍。  一个线程组可以包含其它线程组,它们共同构成一颗树,每个线程组包括初始线程组都会有一个父节点。     线程组内可以通信,但是不能与自己的父线程组通信,也不能与其它线程组通信。     锁机制会给同一个级别高度的节点加锁(注意这里没有权重的概念,不要搞混),并且是从底向上的。有利于快照还是啥的。 。。       

    这里疑惑产生了:   从线程组的角度思考,它有自己的父节点,但是没有直接声明自己的子节点,多个线程组之间是一种树形的关系。   从线程的角度考虑,每个线程有一个线程组,线程内部有一些自己内部的状态。   那么它们二者应该是一种怎么用的关系呢?  并且我们知道当一个线程退出的时候,会将它所在的线程组初始化为空。    思考了一下,认为它们的图形表示类似于多颗高度相同的树并列在一起,同一高度的节点,看其父节点是否一致而判断是否处于同一线程组。   现实生活中有点像 用 那种专门挂内衣内裤袜子的衣架吧,挂杆看作一个父线程组(它还有一个父线程组为空),每个挂上去的衣架为一个线程,每个衣架上的夹子上面的内衣内裤袜子等就是一个线程,它们同时又属于一个线程组。 同理。 。。      继续:

《java源码解析之Thread(二)》

    这是根节点初始化。  可以看到这个根线程组节点为空。  而一级子类线程组为system组。   调用c代码,写的如此清晰。 啧啧

《java源码解析之Thread(二)》

    同样有趣的是它的toString()方法。 

《java源码解析之Thread(二)》

    不解释。  总感觉这个类很重要的样子。 嘿嘿嘿。

    接着去看看神奇的栈帧类:

《java源码解析之Thread(二)》

    这个就写的比较详细了。 为了加深记忆,继续再敲一遍。 除栈顶元素外,所有的栈帧都代表一个方法的调用。   栈帧顶部表示的是正在生成的执行点。 典型情况下,该点位抛出的异常。 继续:

《java源码解析之Thread(二)》

    同样,有趣的toString()方法。  这里阐述一下,为什么说它有趣呢?  因为它把一个对象描述的恰到好处,即简明扼要的给出了类中重要的属性,同时指明了哪些是关键的,重要的信息,同时又没有类中复杂的语句,逻辑调用等。 对我们的学习可谓有着不小的辅助效果。  继续:

《java源码解析之Thread(二)》

    这时构造方法中的一个小小地方,这样以后看到这样的字符串就能大致猜出哪里抛出的了嘻嘻。  到这,栈元素类收藏的属性与方法也看完了。  接下来看看啥呢?

    糟糕,最重要的给忘了。 看看它们的类结构以及依赖图,可不发现最顶层的为 Runnable嘛!!

《java源码解析之Thread(二)》

    走你。  这里是Runnable的官方权威说明。   这里面说明了线程的使用方法的两种方式:实现Runable接口,或者继承Thread类。 它是这样说的:某个实例要想被一个线程执行,则必须实现Runnable接口,并且实现run方法。  这个Runnable接口提供了一种通用的协议使得对象可以执行它们的代码。 但是它们必须是活跃的,就是说它们的线程已经被运行,并且没有终止。 大多数情况下,我们应当实现Runnable方法并覆盖run方法,如果我们我不必用到线程的一些其它方法的时候。 这十分重要,因为一个类不应该继承为子类,除非程序员打算修饰或者优化某个类的基本方法。   这也是为了实现多态。    

    这里我同样产生了一些疑问,在之前观看Thread源码的时候,可以知道一个线程能够存在并不止它的target,也就是要执行的方法,同时还包括一些属性的维护,线程组的管理,线程组树的更新与维护,守护线程的标志,线程名,线程ID等等。  那么这里直接实现runnable接口为什么我们任然称之为新的线程呢?    我猜想是因为:  这里所说的一个新的线程是针对jvm来说,它采用了某种机制,或者说 协议  使得从执行机理上跟thread是一样的。  事实上也确实是一样的,因为 thread的启动方法start方法,除了执行了一个start0外,会由jvm调用  tartet的run方法。  所以这里可以也可以说是新开一个线程了把。 。。。    

    关于线程的问题需要补充一点就是:  基本上大家所熟知的实现线程的方式就两种,一种是  通过是实现 Runnable接口, 另一种就是继承Thread类。   那么我在某些技术论坛或者教程上看到是这样,它们说实现线程的方式由三种,除了前两种外,还有一个就是继承Timetask或者 Callable接口还是怎样,过了很久我快忘了。   不过当时大致看了下,其也是继承了Runable接口的。 但是既然作为了一个技术论坛,我想它也不会空穴来风,一定是有一定道理的。 此外说到这里我想起自己之前的一个实验,那就是是否主线程结束,程序就结束???   通过查找资料和亲身实验,大多数情况是这样的,但是想一想java程序停止的条件: 所有非守护线程都执行完之后程序停止。  也就是说只要存在这样一种情况,主线程已经执行完了,但是还有其他线程正在被运行那么此时程序就不会停止。   举个例子,如上面介绍的TimeTask类,便可以实现,具体的教程网上有 。   再举个例子,springboot启动的时候,可以看到main方法早已被执行完毕,但是程序任未停止。  自己也自己尝试做了一些实验,但是都以失败告终,因为主线程执行完毕总在其它线程执行完毕之后。    之前一直象不太清楚,为什么有些线程可以完成这个实验,大多数线程又不行,但是了解了这些后,我大胆猜想下:满足这个实验有个前提条件就是 被实验的线程要求与主线程不在同一个线程组才行,想一想线程退出的条件吧?  那个exit方法会将本线程的线程组给清空。 而我自己做的实验基本都是在main线程生成的线程,其默认会被放到main线程组中。  或者也可以结合线程组树理解。   

    此外既然接触到了多线程,顺便看一看调试的发现。  之前的理解是:一个java程序至少应该是两个线程再跑,一个是垃圾收集机制的gc线程,它是一个守护线程。 一个是主线程。  这次调试后我产生了丝丝怀疑:

《java源码解析之Thread(二)》

    可以看到这里启动了五个线程,其中三个正在跑,两个在等待。  @466那个便是垃圾收集的线程,@1那个便是主线程了,除了它是非守护线程外,其它都是守护线程。   并且我猜测它们应该都是属于system线程组的线程。   @467那个线程应该是负责处理引用关系的吧,毕竟要见名知意。嘿嘿。  @465可能是负责环境相关的维护吧,信号分发器嘿嘿。  @464 啥监听器之类的,负责处理监听相关的吧哈哈哈。   看一下某次测试打印的堆栈信息:

《java源码解析之Thread(二)》

《java源码解析之Thread(二)》

《java源码解析之Thread(二)》

《java源码解析之Thread(二)》

    这种入栈模式没怎么看懂。

《java源码解析之Thread(二)》

《java源码解析之Thread(二)》

《java源码解析之Thread(二)》    这个线程没见过,以后有机会研究。  (怕是没有机会了,哦  呵呵呵,明年的今天该正儿八经的找实习搞钱了)。

不管怎样把,我这个小菜鸟也跟着大牛的脚步,和那么一丝丝可笑的激情支撑,还是看了一些源码。  这就是所有关于线程的一些理解和见解。   最后做一个总结性的介绍,算是逻辑比较清晰的把,因为之前都是那种随感觉去看的。  我们要有一个清晰的逻辑,那自然就是跟着我们平时练习的痛点入手。  看看,Thread的构造方法:

    空参:

《java源码解析之Thread(二)》

    空参中转:

《java源码解析之Thread(二)》

    空参实际调用(也是其它对外的构造方法的实际调用):

《java源码解析之Thread(二)》

《java源码解析之Thread(二)》

    其它的一些构造方法大同小异,就没看了。

    好了,花了一定的时间进行笔记的整理。  我相信是值得的。

    原文作者:java源码分析
    原文地址: https://blog.csdn.net/qq_36285943/article/details/80040403
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞