概述
当我们在 Java 中执行以下代码时:
java
Thread thread = new Thread(() -> {
System.out.println("Hello from new thread!");
});
thread.start();
表面上只是简单的两行代码,但背后涉及 Java 层 、JVM 层 、操作系统层 三个层面的复杂交互。理解这个过程对于:
- 面试:大厂高频面试题,考察对 JVM 和操作系统的理解深度
- 调优:线程池参数调优、性能问题定位
- 排障:线程相关问题排查
Java 线程模型
在 HotSpot JVM 中,Java 线程采用 1:1 模型,即一个 Java 线程对应一个操作系统原生线程(Native Thread)。

整体调用链路
Thread.start()的完整调用链路可以分为Java层、JVM层、操作系统层和线程运行四个主要阶段。
首先,在Java层 ,应用程序调用Thread.start()方法。该方法内部会调用一个名为start0()的native方法,从而进入本地(Native)代码执行环境。
随后,控制权转移到JVM层(以HotSpot为例) 。start0()方法在JVM中对应的本地函数是JVM_StartThread()。该函数是线程创建的起点,它首先会创建一个JavaThread对象,该对象代表了JVM内部的一个线程。接着,JVM会为这个JavaThread对象关联一个底层操作系统的线程抽象,即OSThread对象。最后,通过调用os::create_thread()这个平台相关的接口,正式向操作系统发起创建线程的请求。
接下来进入操作系统层 。os::create_thread()函数会根据当前的操作系统类型,调用不同的平台API。在Linux系统上,最终会调用pthread_create()函数,而该函数在底层会进一步通过clone()系统调用来创建线程。在Windows系统上,则会调用CreateThread() API,其底层对应的是NtCreateThread系统调用。无论通过哪种路径,最终都会由操作系统内核执行创建线程的核心工作,例如在Linux内核中分配task_struct结构体。
线程被操作系统成功创建并调度执行后,便进入线程运行 阶段。新线程的入口函数是thread_native_entry(),它由JVM设置。该函数负责初始化线程环境,并最终回调到Java层的Thread.run()方法。至此,用户在新线程中定义的run()方法逻辑开始执行,Thread.start()的完整调用链路结束。
下面是 Thread.start() 的完整调用链路:

| 层次 | 关键组件 | 核心功能 |
|---|---|---|
| Java 层 | Thread 类 | 提供面向对象的线程抽象 |
| JVM 层 | JavaThread, OSThread | 管理线程生命周期,连接 Java 与 OS |
| OS 层 | pthread / Windows Thread | 真正的线程创建与调度 |
| 内核层 | task_struct | 内核中的进程/线程描述符 |
Java 层:Thread.start() 源码分析
Thread 类核心结构
java
public class Thread implements Runnable {
// 线程名称
private volatile String name;
// 线程优先级
private int priority;
// 是否为守护线程
private boolean daemon = false;
// 要执行的目标任务
private Runnable target;
// 线程组
private ThreadGroup group;
// 线程ID
private long tid;
// 线程状态(NEW, RUNNABLE, BLOCKED, WAITING, TIMED_WAITING, TERMINATED)
private volatile int threadStatus = 0;
// 与此 Java 线程关联的 OS 线程句柄(JVM 内部使用)
private long eetop; // JavaThread 指针
}
start() 方法源码
java
public synchronized void start() {
// 1. 状态检查:确保线程未启动过
if (threadStatus != 0)
throw new IllegalThreadStateException();
// 2. 将线程加入线程组
group.add(this);
boolean started = false;
try {
// 3. 调用 native 方法真正创建线程
start0();
started = true;
} finally {
try {
if (!started) {
group.threadStartFailed(this);
}
} catch (Throwable ignore) {
// 忽略异常
}
}
}
// native 方法声明
private native void start0();
关键点解析
| 步骤 | 说明 |
|---|---|
| 状态检查 | 确保 threadStatus == 0(NEW 状态),防止重复启动 |
| 加入线程组 | 方便线程管理和资源统计 |
| 调用 start0() | 进入 JVM 层,真正创建操作系统线程 |
要点 :
start()方法是synchronized的,保证线程安全;同一个 Thread 对象只能 start 一次。
JVM 层:Native 方法实现
start0() 的 JNI 注册
在 Thread.c 中,start0 被注册为 native 方法:
c
// jdk/src/share/native/java/lang/Thread.c
static JNINativeMethod methods[] = {
{"start0", "()V", (void *)&JVM_StartThread},
{"stop0", "(Ljava/lang/Object;)V", (void *)&JVM_StopThread},
{"isAlive", "()Z", (void *)&JVM_IsThreadAlive},
{"suspend0", "()V", (void *)&JVM_SuspendThread},
{"resume0", "()V", (void *)&JVM_ResumeThread},
{"setPriority0", "(I)V", (void *)&JVM_SetThreadPriority},
// ...
};
JVM_StartThread 实现
cpp
// hotspot/src/share/vm/prims/jvm.cpp
JVM_ENTRY(void, JVM_StartThread(JNIEnv* env, jobject jthread))
JVMWrapper("JVM_StartThread");
JavaThread *native_thread = NULL;
bool throw_illegal_thread_state = false;
{
MutexLocker mu(Threads_lock);
// 检查线程是否已经启动
if (java_lang_Thread::thread(JNIHandles::resolve_non_null(jthread)) != NULL) {
throw_illegal_thread_state = true;
} else {
jlong size = java_lang_Thread::stackSize(JNIHandles::resolve_non_null(jthread));
// 创建 JavaThread 对象(关键步骤)
native_thread = new JavaThread(&thread_entry, sz);
if (native_thread->osthread() != NULL) {
// 准备启动
native_thread->prepare(jthread);
}
}
}
if (throw_illegal_thread_state) {
THROW(vmSymbols::java_lang_IllegalThreadStateException());
}
// 启动线程
Thread::start(native_thread);
JVM_END
JavaThread 构造过程
cpp
// hotspot/src/share/vm/runtime/thread.cpp
JavaThread::JavaThread(ThreadFunction entry_point, size_t stack_sz) :
Thread() {
initialize(); // 初始化 JavaThread 各种字段
_entry_point = entry_point;
// 调用 os::create_thread 创建操作系统线程
os::create_thread(this, thr_type, stack_sz);
}
OSThread 与 JavaThread 关系
在JVM的线程管理体系中,OSThread与JavaThread是两个紧密关联但又职责分明的核心对象,共同协作以完成Java线程在底层系统的完整映射与执行。OSThread是JVM对操作系统原生线程(例如Linux的pthread或Windows线程)的抽象封装,它主要负责管理与操作系统内核交互的底层细节,例如线程栈的分配、线程优先级设置以及线程在操作系统层面的状态同步。而JavaThread则是JVM内部用于表示一个Java线程的逻辑对象,它承载了Java层面的线程状态、线程局部存储、Java栈帧以及指向目标Java Thread对象的引用等高层信息。
两者的关系可以描述为一种**"代理"或"连接器"关系**。具体来说,一个JavaThread对象内部会持有一个指向其对应OSThread对象的指针。这种设计使得JVM能够通过JavaThread来管理Java层的线程逻辑和状态,同时通过其关联的OSThread来委托执行所有与具体操作系统相关的线程操作。当调用Thread.start()时,JVM会先创建JavaThread来设置好Java环境,然后创建并关联OSThread来向操作系统申请真正的执行实体。当操作系统调度并执行该原生线程时,其入口函数会通过OSThread找到关联的JavaThread,从而最终能定位并执行用户编写的Thread.run()方法。因此,OSThread是JavaThread在操作系统中的"腿"和"手",负责实际的执行和资源管理;而JavaThread是OSThread的"大脑"和"上下文",定义了要执行的任务和所处的Java虚拟机环境。

操作系统层:线程创建机制
Linux 平台实现
cpp
// hotspot/src/os/linux/vm/os_linux.cpp
bool os::create_thread(Thread* thread, ThreadType thr_type, size_t stack_size) {
OSThread* osthread = new OSThread(NULL, NULL);
thread->set_osthread(osthread);
// 设置线程属性
pthread_attr_t attr;
pthread_attr_init(&attr);
pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
// 设置栈大小
pthread_attr_setstacksize(&attr, stack_size);
ThreadState state = ALLOCATED;
osthread->set_state(state);
// 核心:调用 pthread_create 创建线程
int ret = pthread_create(&tid, &attr,
(void* (*)(void*)) thread_native_entry,
thread);
pthread_attr_destroy(&attr);
if (ret != 0) {
return false;
}
// 保存线程ID
osthread->set_pthread_id(tid);
// 等待线程初始化完成
{
Monitor* sync = osthread->startThread_lock();
MutexLockerEx ml(sync, Mutex::_no_safepoint_check_flag);
while (osthread->get_state() == ALLOCATED) {
sync->wait(Mutex::_no_safepoint_check_flag);
}
}
return true;
}
线程入口函数
cpp
// hotspot/src/os/linux/vm/os_linux.cpp
static void *thread_native_entry(Thread *thread) {
// 初始化线程局部存储
thread->initialize_thread_current();
OSThread* osthread = thread->osthread();
// 设置线程状态为 INITIALIZED
osthread->set_state(INITIALIZED);
// 通知创建线程:子线程已初始化完成
{
Monitor* sync = osthread->startThread_lock();
MutexLockerEx ml(sync, Mutex::_no_safepoint_check_flag);
sync->notify();
}
// 等待真正启动信号
{
MutexLockerEx ml(sync, Mutex::_no_safepoint_check_flag);
while (osthread->get_state() == INITIALIZED) {
sync->wait(Mutex::_no_safepoint_check_flag);
}
}
// 调用 thread->run(),最终执行 Java 的 run() 方法
thread->run();
return 0;
}
pthread_create 到 clone() 系统调用
clone() 系统调用的关键标志
线程创建时,clone() 使用以下标志:
c
clone(CLONE_VM | CLONE_FS | CLONE_FILES | CLONE_SIGHAND |
CLONE_THREAD | CLONE_SYSVSEM | CLONE_SETTLS |
CLONE_PARENT_SETTID | CLONE_CHILD_CLEARTID,
child_stack, &parent_tid, &child_tid, tls);
| 标志 | 含义 |
|---|---|
CLONE_VM |
共享虚拟内存空间 |
CLONE_FS |
共享文件系统信息 |
CLONE_FILES |
共享文件描述符表 |
CLONE_SIGHAND |
共享信号处理程序 |
CLONE_THREAD |
放入同一线程组 |
CLONE_SETTLS |
设置线程局部存储 |
Windows 平台实现
cpp
// hotspot/src/os/windows/vm/os_windows.cpp
bool os::create_thread(Thread* thread, ThreadType thr_type, size_t stack_size) {
OSThread* osthread = new OSThread(NULL, NULL);
thread->set_osthread(osthread);
// 调用 Windows API 创建线程
HANDLE thread_handle = (HANDLE)_beginthreadex(
NULL, // 安全属性
(unsigned)stack_size, // 栈大小
thread_native_entry, // 入口函数
thread, // 参数
CREATE_SUSPENDED, // 创建后挂起
&thread_id // 线程ID
);
osthread->set_thread_handle(thread_handle);
osthread->set_thread_id(thread_id);
return true;
}
线程状态转换
Java 线程状态 vs OS 线程状态

状态对应关系表
| Java 线程状态 | OS 线程状态 | 说明 |
|---|---|---|
| NEW | 不存在 | Java 对象已创建,OS 线程未创建 |
| RUNNABLE | Running/Ready | 可运行状态(含正在运行和等待调度) |
| BLOCKED | Sleeping | 阻塞在 synchronized |
| WAITING | Sleeping | 无限期等待 |
| TIMED_WAITING | Sleeping | 有限期等待 |
| TERMINATED | Zombie→不存在 | 执行结束 |
内存布局分析
线程栈内存结构

线程创建的内存开销
| 组件 | 内存大小 | 说明 |
|---|---|---|
| 线程栈 | 1MB (默认) | -Xss 参数可调整 |
| Thread 对象 | ~360 字节 | Java 堆内存 |
| JavaThread | ~2KB | JVM 内部 C++ 对象 |
| OSThread | ~200 字节 | 操作系统线程元数据 |
| 内核栈 | 8KB-16KB | 内核态执行使用 |
要点:创建线程的开销主要在于:
- 栈内存分配(1MB 默认)
- 系统调用开销(用户态→内核态切换)
- 内核资源分配(task_struct 等)
常见问题
为什么要调用 start() 而不是直接调用 run()?
java
// 错误方式:直接调用 run()
Thread t = new Thread(() -> doSomething());
t.run(); // 在当前线程执行,没有创建新线程
// 正确方式:调用 start()
t.start(); // 创建新的 OS 线程执行
答案:
run()只是普通方法调用,在当前线程执行start()会调用 native 方法创建新的操作系统线程,新线程中执行run()
一个线程可以 start 两次吗?
java
Thread t = new Thread(() -> {});
t.start();
t.start(); // 抛出 IllegalThreadStateException
答案 :不可以。start() 方法会检查 threadStatus,非 NEW 状态(0)会抛出异常。
Java 线程和操作系统线程是什么关系?
答案 :HotSpot JVM 采用 1:1 模型:
- 每个 Java Thread 对应一个 OS Native Thread
- 线程调度完全由操作系统负责
- Java 线程状态是对 OS 线程状态的抽象封装
创建线程的成本有哪些?
答案:

为什么推荐使用线程池?
答案:
- 复用线程:避免频繁创建/销毁的开销
- 控制并发数:防止线程过多导致资源耗尽
- 统一管理:方便监控、调优
总结

这是小编最近一次面试的时候,遇到的问题,面试的时候回答的不是很好,复盘之后,写了这篇笔记进行记录。
参考资料
- OpenJDK 8 源码:Thread.java
- HotSpot 源码:thread.cpp
- Linux 内核:kernel/fork.c
- POSIX 线程规范:pthread_create 手册