Java 线程创建的完整链路:从 Java 层 → JVM 层 → 操作系统层

概述

当我们在 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() 系统调用

flowchart LR A["pthread_create()"] --> B["glibc 包装"] B --> C["clone() 系统调用"] C --> D["内核 do_fork()"] D --> E["copy_process()"] E --> F["创建 task_struct"] F --> G["设置 CLONE_THREAD 标志"] G --> H["共享地址空间、文件描述符等"] style A fill:#e3f2fd style C fill:#fff3e0 style D fill:#ffebee style F fill:#e8f5e9

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 内核态执行使用

要点:创建线程的开销主要在于:

  1. 栈内存分配(1MB 默认)
  2. 系统调用开销(用户态→内核态切换)
  3. 内核资源分配(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 线程状态的抽象封装

创建线程的成本有哪些?

答案

为什么推荐使用线程池?

答案

  1. 复用线程:避免频繁创建/销毁的开销
  2. 控制并发数:防止线程过多导致资源耗尽
  3. 统一管理:方便监控、调优

总结

这是小编最近一次面试的时候,遇到的问题,面试的时候回答的不是很好,复盘之后,写了这篇笔记进行记录。

参考资料

相关推荐
zfj3212 小时前
排查java应用内存溢出的工具和方法
java·开发语言·jvm·内存溢出
历程里程碑2 小时前
C++ 7vector:动态数组的终极指南
java·c语言·开发语言·数据结构·c++·算法
ss2732 小时前
高并发读场景:写时复制容器(Copy-On-Write)
java·开发语言·rpc
文心快码BaiduComate2 小时前
我用文心快码Spec 模式搓了个“pre作弊器”,妈妈再也不用担心我开会忘词了(附源码)
前端·后端·程序员
aiopencode2 小时前
iOS 性能监控 运行时指标与系统行为的多工具协同方案
后端
一人の梅雨2 小时前
淘宝商品视频接口深度解析:从视频加密解密到多端视频流重构
java·开发语言·python
是喵斯特ya2 小时前
java反序列化漏洞解析+URLDNS利用链分析
java·安全
她说..2 小时前
MySQL数据处理(增删改)
java·开发语言·数据库·mysql·java-ee
BD_Marathon2 小时前
【JavaWeb】ServletContext_域对象相关API
java·开发语言