知识推理
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线程不可以吗?以下有几个关键原因:
- 最直接的就是屏蔽底层:Java程序员只需要关心Java层面、而JVM、OS是对开发者是透明的
- 便于分层开发:Java开发者直接操作Java线程启动的api,而内部是由JVM来进行调用OS系统调用接口,JVM开发者对于如何调用,不影响Java开发者
其实本质上是分层开发的好处:
- 解耦
- 可插拔、可替换、可扩展、可复用
- 各层互相透明
无论是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()底层做了什么,其中涉及到了:
- 应用程序如何创建一个线程
- 分层设计的好处
- 对于Java开发者线程执行的入口
- 通过JVM源码确定线程创建的方式
建议大家跟着我的思路从JDK源码到JVM源码跟踪一遍,这样才能加深以上内容的理解