为什么启动线程是start方法?
为什么启动线程是start方法
十年可见春去秋来,百年可证生老病死,千年可叹王朝更替,万年可见斗转星移。
凡人如果用一天的视野,去窥探百万年的天地,是否就如同井底之蛙?
背景:启动线程是start() 还是run() 方法?相信这个问题很多人都知道是start(),但是如果我再问下去呢,为什么是start()?你会如何作答呢?
一、理论课
当用start()开始一个线程后,线程就进入就绪状态,使线程所代表的虚拟处理机处于可运行状态,这意味着它可以由JVM调度并执行;但是这并不意味着线程就会立即运行,只有当cpu分配时间片时,这个线程获得时间片时,才开始执行run()方法;start()方法去调用run(),而run()方法则是需要去重写的,其包含的是线程的主体(真正的逻辑)。
二、线程六大状态
Java 中,定义了 6 种线程状态,NEW、RUNNABLE、BLOCKED、WAITING、TIMED_WAITING和TERMINATED。
1 public enum State{
2 NEW,
3 RUNNABLE,
4 BLOCKED,
5 WAITING,
6 TIMED_WAITING,
7 TERMINATED;
8 }
6 种线程状态关系图
三、代码层次
杨总说的,一切问题归咎于源码!
start 方法的源码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 | public synchronized void start() { /** * This method is not invoked for the main method thread or "system" * group threads created/set up by the VM. Any new functionality added * to this method in the future may have to also be added to the VM. * * A zero status value corresponds to state "NEW". */ // 未初始化则抛异常 if (threadStatus != 0 ) throw new IllegalThreadStateException(); /* Notify the group that this thread is about to be started * so that it can be added to the group's list of threads * and the group's unstarted count can be decremented. */ group.add( this ); // 是否启动标志符 boolean started = false ; try { /** * start0() 是启动多线程的关键 * 执行完成之后,新的线程已经在运行了 * 这里会创建一个新的线程,是一个 native 方法 */ start0(); // 主线程执行 started = true ; } finally { try { if (!started) { group.threadStartFailed( this ); } } catch (Throwable ignore) { /* do nothing. If start0 threw a Throwable then it will be passed up the call stack */ } } } |
run方法源码:
1 2 3 4 5 6 7 | @Override public void run() { // 简单的运行,不会新起线程,target 是 Runnable if (target != null ) { target.run(); } } |
从start()和run()方法的源码可以看出,start方法的源码也没几行代码,最主要的是 start0() 方法;run() 方法的源码也比较简单的,就是一个普通方法的调用;重点是这个 start0() 方法,她是真正实现多线程的关键。
start0方法源码:
1 2 | // native :就是本地方法 private native void start0(); |
关于native方法简述:
1、被native关键字修饰的方法叫做本地方法,本地方法和其它方法不一样,本地方法意味着和平台有关,因此使用了native的程序可移植性都不太高;
2、native方法在JVM中运行时数据区也和其它方法不一样,它有专门的本地方法栈;
3、native方法主要用于加载文件和动态链接库,由于Java语言无法访问操作系统底层信息(比如:底层硬件设备等),这时候就需要借助C语言来完成了;
4、被native修饰的方法可以被C语言重写。
Java 跨平台图
在start()中start0 被标记成 native ,也就是本地方法,并不需要我们去实现或者了解,只要了解下为什么 start0() 会标记成 native ?
如上图,start() 方法调用 start0() 方法后,该线程并不一定会立马执行,只是将线程变成了可运行状态(NEW ---> RUNNABLE);具体什么时候执行,取决于 CPU ,由 CPU 统一调度;我们又知道 Java 是跨平台的,可以在不同系统上运行,每个系统的 CPU 调度算法不一样,所以就需要做不同的处理,这件事情就只能交给 JVM 来实现了,start0() 方法自然就表标记成了 native。
四、总结
Java 中实现真正的多线程是 start 中的 start0() 方法,run() 方法只是一个包含业务逻辑普通的方法;start是启动多线程的唯一方式,其使得线程由创建态到就绪态,而这个线程是否被运行是由系统调度所决定的。
十年可见春去秋来
百年可证生老病死
千年可叹王朝更替
万年可见斗转星移
凡人如果用一天的视野
去窥探百万年的天地
是否就如同井底之蛙?
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 分享 3 个 .NET 开源的文件压缩处理库,助力快速实现文件压缩解压功能!
· Ollama——大语言模型本地部署的极速利器
· DeepSeek如何颠覆传统软件测试?测试工程师会被淘汰吗?