我们一起看源码<Thread>源码逐行解析

朝闻道,夕死可矣

红色:重点方法名 或者是 注释解析
黄色:《书名
蓝色:专有名词
绿色:源码解析
紫色:源码变量含义
棕色:线程状态
粉色:上文内容

一英文注释

在这里插入图片描述
java虚拟机允许程序中多个线程运行

在这里插入图片描述

每个线程都有一个优先级,优先级高的线程优先运行与低优先级线程
每个线程都可以是守护线程
在一个线程里面创建子线程时,子线程继承父线程优先级,并且当父线程是守护线程时子线程也是守护线程

在这里插入图片描述

当JVM虚拟机启动时,通常会有一个非守护线程来调用某个类的指定main方法
JVM虚拟机继续执行直到碰到下面 两种 情况:

情况一:
在这里插入图片描述

  • 已调用 Runtime 类的退出方法 , 并且安全管理器已允许退出操作发生.
    1.1Runtime解释Runtime的退出方法

1.1Runtime

这个Runtime类是干什么的?
在这里插入图片描述
Runtime是用来让
应用程序与应用程序运行的 环境进行交互
在Runtime类中有一个exit()方法,可以停在正在运行的虚拟机
在这里插入图片描述
在Runtime类只有一种初始化方式:
哪就是调用其静态的getRuntime()方法
在这里插入图片描述

让我们一起来试一下这个Runtime的exit()方法吧
在这里插入图片描述
可以看到,jvm虚拟机停在后,15行代码没有被执行

情况二:
在这里插入图片描述

  • 除了守护线程的其它线程都死亡时,或者是return或者是抛出异常时

在这里插入图片描述
有两种方式创建新线程,
1.将类声明成Thread的子类,这个子类覆盖Thread的run方法,
例如,计算大于规定值的素数的线程可以写成如下:
在这里插入图片描述在这里插入图片描述
2. 声明一个实现 Runnable 接口的类,.实现类实现run方法,
例如,如下
在这里插入图片描述

在这里插入图片描述
每个线程都一个名称,如果创建线程时没有指定名称,系统会自动生成一个名称
除非另有说明,否则将空参数传递给此类中的构造函数或方法将导致抛出 NullPointerException。


二变量的初始化

在这里插入图片描述

为了搞清楚这个4行代码,我们先来了解一下本地方法
本地方法是指在Java类用被native修饰的没有实现的方法

在《深入Java虚拟机》这个本书的1.3.1对Java方法的描述:
Java有两种方法:Java方法本地方法.
Java方法是由Java语言编写,编译成字节码,存储在class文件中.
格式与平台无关
本地方法是由其他语言编写的,编译成和处理器相关的机器代码,
格式是各个平台专有的,保存在动态连接数据库中,
当被调用时,虚拟机装载包含这个本地方法的动态连接数据库
.
本地方法 是 联系Java程序和底层主机操作系统的连接方法

registerNatives()方法就是一个用来注册本地方法的,

Q1 注册了哪些方法?
注册的方法就是该类所包含的除了registerNatives()方法以外的所有本地方法

Q2 为什么需要注册?
在《The Java Native Interface Programmer’s Guide and Specification》这本书的8.3Registering Native Methods来回答这个问题
.
一个java程序要想调用一个本地方法,需要两步

1,通过System.loadLibrary()将包含本地方法实现的动态文件加载进内存

2,虚拟机在加载的动态文件中定位并连接该本地方法,从而得以执行本地方法

registerNatives()方法的作用就是取代第二步,
让程序主动将本地方法链接到调用方,
当Java程序需要调用本地方法时就可以直接调用
而不需要虚拟机再去定位并链接
.
书中还总结了所用registerNatives()方法的四点好处

  • 在类被加载时就主动将本地方法链接到调用方,比如方法被使用时才由虚拟机来定位链接更方便有效
  • 如果本地方法在程序运行中更新了,可以通过调用registerNatives()进行更新
  • Java程序需要调用一个本地应用提供的方法时,因为虚拟机只会检索本地动态库,因而虚拟机是无法定位到本地方法实现的,这个时候只能使用registerNatives()方法进行主动链接
  • 通过registerNatives()方法在定义本地方法实现的时候,可以不遵守JNI命名规范,

JNI命名规范:要求本地方法名由 包名+方法名 构成,
例如,Tread类中的registerNatives()方法对应的本地方法名叫
Java_java_lang_Thread_registerNatives
Object类中的registerNatives()方法对应的本地方法名叫
Java_java_lang_Object_registerNatives

Q3 具体怎么注册的?
这个问题涉及到registerNatives()方法底层C++源码实现,
点击跳转–>关于调用约定(cdecl、fastcall、、thiscall) 的一点知识(用汇编来解释)good

点击跳转–>使用JNI_OnLoad动态注册函数

点击跳转–>【JVM源码探秘】深入registerNatives()底层实现

回归主线Thread类在这里插入图片描述

那么这个Runnable又是干什么的?

2.1Runnable

2.1.1 Runnable注释
在这里插入图片描述
Runnable接口的实例由线程所执行的类实现,
这个类必须定义一个名为 run() 的无参方法
.
如果 Runnable由Thread实现,处于 运行状态 仅意味着线程已启动且尚未停止
.
此外,Runnable提供了使类处于 运行状态 而不是子类化Thread的方法
通过实例化Thread实例并将自身作为目标传入,实现Runnbale的了可以在不继承Thread类的情况下运行
.
然而大多数情况下,只覆盖 run() 方法而不是覆盖其它的Thread的方法,就可以使用Runanle接口

2.1.2 Runnable注解
在这里插入图片描述
此注解是 JDK8新特性 中的 函数式接口 的伴生品
该注解的特点:

  • 只能标记在有且仅有一个抽象方法的接口上
  • JDK8接口中的静态方法和默认方法,都不算是抽象方法
  • 接口默认继承Java.lang.Object,所以如果接口显示声明覆盖了Object方法,那也不算抽象方法
  • 改注解不是必须的,如果一个接口符合函数式接口,那么加不加本注解都没有影响,加上是为了让编译器进行检查
    相反,如果不是函数式接口 而加了@FunctionInterface那么编译器会报错
    在这里插入图片描述

函数式接口的定义是:
只包含一个抽象方法的接口,
但是容许包含 默认方法静态方法

在这里插入图片描述

函数式接口的用途
主要用在Lambda表达式
在这里插入图片描述
这样就可以使用Lambda表达式来表示该接口的一个实现
在这里插入图片描述

Java8中常用的函数式接口:
在这里插入图片描述

2.1.3 Runnable 方法
在这里插入图片描述
当使用实现Runable接口的对象创建线程时,
启动线程会让线程调用 run()方法
run()里边可以写一些线程采取的动作

回归主线Thread类在这里插入图片描述

2.2ThreadGroup

2.2.1注释
在这里插入图片描述
ThreadGroup代表一组线程
一个线程组还可以包含其他线程组
线程组形成一棵树,其中除了初始线程组之外的每个线程组都有一个父线程组
一个线程可以访问它自己的线程组的信息,但不能访问它的线程组的父线程组
或任何其他线程组的信息
在这里插入图片描述

2.2.2构造
ThreadGroup共有4个构造
在这里插入图片描述
这4个构造底层就是下图这个构造

在这里插入图片描述
从这个构造中,我们可以得出:
一个线程组核心信息是:名称,优先级,是否守护,父线程组,子线程组
在这里插入图片描述

还有一个默认的构造,用来创建系统线程组
在这里插入图片描述

2.2.3名称
在这里插入图片描述

2.2.4优先级

在这里插入图片描述

2.2.5是否为守护线程
在这里插入图片描述

2.2.6父线程组
在这里插入图片描述
21

2.2.7add方法

add(ThreadGroup g)添加线程组
在这里插入图片描述
在这里插入图片描述

add(Thread t)添加线程
在这里插入图片描述
在这里插入图片描述

所以通过以上方法组成了线程组的树形结构
也就是说

  • 每个线程组知道自己包含了多少线程,有哪些线程
  • 每个线程组知道自己包含了多少线程组,有哪些线程组
    在这里插入图片描述

2.2.8子线程组相关方法
既然是树形结构,哪我们看看枚举节点的需求
如图所示:将6个方法看出两组方法
在这里插入图片描述

第一组
2.2.8.1enumerate(Thread list[])
在这里插入图片描述
在这里插入图片描述

2.2.8.2 enumerate(Thread list[], boolean recurse)
在这里插入图片描述

2.2.8.3enumerate(Thread list[], int n, boolean recurse
在这里插入图片描述

第二组
enumerate(ThreadGroup list[])
enumerate(ThreadGroup list[], boolean recurse)
enumerate(ThreadGroup list[], int n, boolean recurse)
和第一组原理相同,

2.2.9线程组的interrupt()方法
在这里插入图片描述

ThreadGroup结尾了,我们继续看Thread中定义的变量

回归主线Thread类,书接166行 private ThreadGroup group;
在这里插入图片描述

在这里插入图片描述>在这里插入图片描述

在这里插入图片描述

![在这里插入图片描述](https://img-blog.csdnimg.cn/ceec655f59be403b9f9ee3a9e40c84fa.png)下一个
在这里插入图片描述
当调用java.util.concurrent.locks.LockSupport.park方法时需要的参数
由(私有)的java.util.concurrent.locks.LockSupport.setBlocker方法设置本参数
使用 java.util.concurrent.locks.LockSupport.getBlocker 访问
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述


三本地方法

3.1currentThread()方法

在这里插入图片描述

3.2yield()方法

在这里插入图片描述

yield()让当前正在运行的线程回到 就绪状态,来让具有相同优先级的其它线程获得运行的机会,以 轮换执行 ,然而CPU可能会忽略yield()方法,因为进入 就绪状态 的线程很可能再次被CPU选中并运行

3.3sleep()方法

在这里插入图片描述
sleep(long millis)使当前正在执行的线程休眠(暂时停止执行)指定的毫秒数,进入 超时等待状态
这个取决于系统计时器和调度查询的精度和准确性.
这个线程不会失去任何监视器的所有权


四Java方法

4.1sleep(long millis,int nanos)方法

在这里插入图片描述在这里插入图片描述

4.2SecurityManager类

在进入init()方法之前,先来了解一下SecurityManager类,
.
4.2.1SecurityManager类的作用
在运行未知的Java程序时,
该程序可能有恶意代码(删除系统文件,重启系统等),
为了防止运行恶意代码对系统产生影响,
需要对运行的代码的权限进行控制
SecurityManager类就是 Java安全管理器
.
4.2.2 启动安全管理器

方式一:启动参数
启动程序时通过附加参数启动Java安全管理器
-Djava.security.manager
若要同时制定配置文件的位置需要如下示例:
-Djava.security.manager
-Djava.security.policy="E:/java.policy"

方式二:编码方式启动
在这里插入代码片

4.3init()方法及其重载方法

在这里插入图片描述
在这里插入图片描述

  • 4
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值