new Thread().start()底层做了什么?

知识推理

new Thread().start()对于Java开发者来说,都知道是启动一个Java线程,但是创建一个线程的真正的方式是调用OS的系统调用,例如sys_clone、sys_fork;OS会给线程分配一个task_struct结构,用于在内存中存放线程的上下文信息,也是被OS任务调度的基本单位,下图是对task_struct的整体概括

上面是为了解释想要创建一个线程,能被OS调度的线程,是需要调用系统调用来创建,所以无论上层应用程序是Java、GO、C、C++、Python所构建的,他们只要创建线程都需要调用OS的系统调用接口;那么为什么还会有Java线程这个说法呢?直接就操作OS线程不可以吗?以下有几个关键原因:

  1. 最直接的就是屏蔽底层:Java程序员只需要关心Java层面、而JVM、OS是对开发者是透明的
  2. 便于分层开发:Java开发者直接操作Java线程启动的api,而内部是由JVM来进行调用OS系统调用接口,JVM开发者对于如何调用,不影响Java开发者

其实本质上是分层开发的好处:

  1. 解耦
  2. 可插拔、可替换、可扩展、可复用
  3. 各层互相透明

无论是Spring开发中使用的三层架构、还是OSI七层模型、还是Graalvm的可插拔等,这些本质上都是利用分层开发的好处,在最初架构设计上就采用分层思想

上文我通过自底向上的讲解了其实创建线程是需要调用创建线程的系统调用接口,而Java的线程创建当然逃脱不了,为什么有Java线程这一概念,本质上是一种分层思想的体现,下面我将从源码角度,自顶向上论证

知识论证

arduino 复制代码
Thread.java
public synchronized void start() {
  //....
    start0();
  //....
 }
 
 
Thread.c
static JNINativeMethod methods[] = {
  //....
    {"start0",           "()V",        (void *)&JVM_StartThread},
  //....
};
  

上面是从Java到JVM的调用链路,下面是核心代码,使用图片标注方式更清晰

接下来调用JavaThread的构造器 os:create_thread会调用pthread_create,这个函数是glibc函数库对OS系统调用的封装,所以当这个函数执行成功后,底层的OS线程就成功创建出来,然后会以你传入的java_start的函数地址作为执行入口

java_start这个函数作为OS线程启动后的执行入口,对于Java开发者来说,我们Java线程的执行入口是run()方法,那么这个函数应该会调用我们Java开发者写的run()方法,请看接下来的调用过程来论证

java_start调用threa->run();

threa->run()调用thread_main_inner();

thread_main_inner()调用entry_point();

还记得我们在上面在JVM代码中new Thread的构造器中set_entry_point吗,其中就将我们java中Thread对象的run方法作为了JavaCalls::call_virtual的方法名,也即接下来要调用的方法

自此调用到了我们Java开发者编写的run方法内; 所以也可以得知,创建Java线程的方式有几种? 根据上面的推理,我们知道在Java中启动一个线程的方式只有一个,调用Thread.start()方法,start()会调用start0(),进入JVM创建线程的过程,当线程创建完毕会调用run方法,而相当于控制了run方法就相当于控制了线程执行的入口

那么有2种方式,直接继承Thread,重写run方法即可,还一种是 在new Thread时,传入Runnable对象,然后将方法执行逻辑写在Runnable对象的run方法中即可; 网上讨论的还有其它的方式本质上传入的还是Runnable的子类 例如:

kotlin 复制代码
public class FutureTask<V> implements RunnableFuture<V>

public interface RunnableFuture<V> extends Runnable, Future<V> 

而Callable接口并没有继承Runnable接口

csharp 复制代码
public interface Callable<V> {

而在JDK中只有FutureTask其中有一个构造器允许Callable类型的对象传入,所以Callable只能与FutureTask配对使用

ini 复制代码
public FutureTask(Callable<V> callable) {
    if (callable == null)
        throw new NullPointerException();
    this.callable = callable;
    this.state = NEW;       // ensure visibility of callable
}

总结

以上从自底向上和自顶向下分析了new Thread().start()底层做了什么,其中涉及到了:

  1. 应用程序如何创建一个线程
  2. 分层设计的好处
  3. 对于Java开发者线程执行的入口
  4. 通过JVM源码确定线程创建的方式

建议大家跟着我的思路从JDK源码到JVM源码跟踪一遍,这样才能加深以上内容的理解

相关推荐
码路飞5 小时前
GPT-5.4 Computer Use 实战:3 步让 AI 操控浏览器帮你干活 🖥️
java·javascript
祈安_6 小时前
Java实现循环队列、栈实现队列、队列实现栈
java·数据结构·算法
皮皮林55118 小时前
拒绝写重复代码,试试这套开源的 SpringBoot 组件,效率翻倍~
java·spring boot
顺风尿一寸1 天前
从 Java NIO poll 到 Linux 内核 poll:一次系统调用的完整旅程
java
程途知微1 天前
JVM运行时数据区各区域作用与溢出原理
java
华仔啊1 天前
为啥不用 MP 的 saveOrUpdateBatch?MySQL 一条 SQL 批量增改才是最优解
java·后端
xiaoye20181 天前
Lettuce连接模型、命令执行、Pipeline 浅析
java
beata1 天前
Java基础-18:Java开发中的常用设计模式:深入解析与实战应用
java·后端
Seven971 天前
剑指offer-81、⼆叉搜索树的最近公共祖先
java