
线程创建过程前言Java线程创建过程一、 整体调用链路概述二、 Java 层与 JNI 桥接层1. Java 源码src/share/classes/java/lang/Thread.java2. JNI 映射源码jdk/src/share/native/java/lang/Thread.c三、 JVM 核心业务逻辑层1. JVM 入口源码hotspot/src/share/vm/prims/jvm.cpp2. JavaThread 构造源码hotspot/src/share/vm/runtime/thread.cpp四、 操作系统抽象层 (Linux 特化)源码hotspot/src/os/linux/vm/os_linux.cpp五、 线程启动与回调生命周期1. 哨兵启动函数java_start (os_linux.cpp)2. 父线程的放行信号Thread::start (thread.cpp)3. 回调 Java 层 run() 方法thread.cpp六、 Linux 内核与系统调用底层Linux 内核中的 clone 标志位分析总结全链路图表前言本文旨在记录近期研读Java源码的学习心得与疑难问题。由于个人理解水平有限文中内容难免存在疏漏恳请读者不吝指正。Java线程创建过程一、 整体调用链路概述在 OpenJDK 8中Java 线程java.lang.Thread的创建与启动是一次典型的跨语言、跨内核边界的协作过程。其核心逻辑并非在 Java 中实现而是通过 JNIJava Native Interface下沉到 JVM 的 C 运行时最终通过 glibc 库调用 Linux 内核的clone()系统调用来创建轻量级进程LWP。整体调用链条如下[Java 层] Thread.start() └─── Thread.start0() (Native 方法) │ [JNI 层] Thread.c - JVM_StartThread │ [JVM 层] jvm.cpp - JVM_StartThread() └─── thread.cpp - new JavaThread() └─── os_linux.cpp - os::create_thread() │ [OS/库层] └─── glibc - pthread_create() └─── Linux 内核 - clone() 系统调用二、 Java 层与 JNI 桥接层Java 层的Thread.start()是线程生命的起点。为了防止线程被重复启动内部维护了一个threadStatus状态机。1. Java 源码src/share/classes/java/lang/Thread.javapublicsynchronizedvoidstart(){// 如果线程状态不是 NEW (0)说明已经启动过或已结束直接抛出异常if(threadStatus!0)thrownewIllegalThreadStateException();// 通知线程组该线程即将被启动将其加入线程组的未启动线程计数中group.add(this);booleanstartedfalse;try{// 调用底层 Native 方法进入 JVM 内部start0();startedtrue;// 标记启动成功}finally{try{if(!started){// 如果启动失败通知线程组进行清理并递减未启动线程计数group.threadStartFailed(this);}}catch(Throwableignore){// 忽略清理期间抛出的异常}}}// 核心 Native 方法映射privatenativevoidstart0();2. JNI 映射源码jdk/src/share/native/java/lang/Thread.cJVM 在启动时会加载核心类库并通过registerNatives将 Java 的start0映射到 C 层的JVM_StartThread函数。#includejni.h#includejvm.h#includejava_lang_Thread.h// 将 Java 方法名、方法签名与 C 函数指针进行绑定staticJNINativeMethod methods[]{{start0,()V,(void*)JVM_StartThread},{stop0,(OBJ)V,(void*)JVM_StopThread},{isAlive,()Z,(void*)JVM_IsThreadAlive},// ... 其他 native 方法省略};JNIEXPORTvoidJNICALLJava_java_lang_Thread_registerNatives(JNIEnv*env,jclass cls){// 注册本地方法映射表(*env)-RegisterNatives(env,cls,methods,sizeof(methods)/sizeof(JNINativeMethod));}三、 JVM 核心业务逻辑层当start0()被触发后执行流程流转到 JVM 容器内HotSpot 虚拟机。此处完成 JavaThread 对象的构建、栈内存分配分配以及与底层 OS 线程的绑定。1. JVM 入口源码hotspot/src/share/vm/prims/jvm.cppJVM_ENTRY(void,JVM_StartThread(JNIEnv*env,jobject jthread))JVMWrapper(JVM_StartThread);JavaThread*native_threadNULL;// 1. 检查 Java 线程对象是否已经被创建过底层 C 线程双重检查boolthrow_illegal_thread_statefalse;{// 获取 Threads_lock 互斥锁保证线程安全MutexLockermu(Threads_lock);// 如果对应的 C JavaThread 已经存在说明该线程已经 start 过了if(java_lang_Thread::thread(JNIHandles::resolve_non_null(jthread))!NULL){throw_illegal_thread_statetrue;}else{// 2. 获取 Java 线程对象中定义的栈大小如果在 new Thread 时传入了 stackSizejlong sizejava_lang_Thread::stackSize(JNIHandles::resolve_non_null(jthread));size_t szsize0?(size_t)size:0;// 3. 创建核心的 C JavaThread 实例// thread_entry 为线程的入口包装函数后续会回调 Java 的 Thread.run()native_threadnewJavaThread(thread_entry,sz);// 如果分配内存失败如 OOM 或无法分配内核线程native_thread 会为 NULLif(native_thread-osthread()!NULL){// 建立 Java 层 Thread 对象与 C 层 JavaThread 对象的双向绑定native_thread-prepare(jthread);}}}// 4. 异常处理抛出非法的线程状态异常if(throw_illegal_thread_state){THROW(vmSymbols::java_lang_IllegalThreadStateException());}// 5. 再次校验 C 线程以及底层的 OSThread 是否成功创建if(native_threadNULL||native_thread-osthread()NULL){if(native_thread!NULL){// 释放内存并清理deletenative_thread;}// 抛出最常见的无法创建本地线程的 OutOfMemoryErrorTHROW_MSG(vmSymbols::java_lang_OutOfMemoryError(),unable to create new native thread);}// 6. 将线程状态设置为 ALLOCATED并正式启动底层的 OS 线程native_thread-set_thread_state(_thread_in_vm);Thread::start(native_thread);JVM_END2. JavaThread 构造源码hotspot/src/share/vm/runtime/thread.cpp在new JavaThread的构造函数中HotSpot 会调用操作系统相关的接口去执行真正的线程创建。JavaThread::JavaThread(ThreadFunction entry_point,size_t stack_sz):Thread(){// 初始化 JavaThread 的内部成员变量initialize();_anchor.clear();_entry_pointentry_point;// 保存线程入口即 thread_entry// 设置线程类型为 java_threados::ThreadType thr_typeos::java_thread;// 核心步骤调用操作系统抽象层 (OS Layer) 的创建接口// 将当前的 JavaThread 指针传过去以便底层 OS 线程能与之关联boolos_alloc_resultos::create_thread(this,thr_type,stack_sz);if(!os_alloc_result){// 如果 OS 分配失败直接返回上层检测到 osthread() 为 NULL 就会抛出 OOMreturn;}}四、 操作系统抽象层 (Linux 特化)HotSpot 针对不同的操作系统Linux、Windows、Solaris有不同的实现。在 Linux 环境下对应的是os_linux.cpp。源码hotspot/src/os/linux/vm/os_linux.cpp在os::create_thread中JVM 会调用 POSIX 线程库glibc 的 NPTL的pthread_create。boolos::create_thread(Thread*thread,ThreadType thr_type,size_t stack_size){assert(thread-osthread()NULL,caller responsible);// 1. 分配一个 OSThread 对象用来记录操作系统层面的线程信息如 tid, 句柄等OSThread*osthreadnewOSThread(NULL,NULL);if(osthreadNULL){returnfalse;// 内存分配失败}// 设置初始线程状态为 ALLOCATEDosthread-set_state(ALLOCATED);// 将 OSThread 绑定到 JavaThread 对象中thread-set_osthread(osthread);// 2. 初始化 pthread 属性对象pthread_attr_t attr;pthread_attr_init(attr);pthread_attr_setdetachstate(attr,PTHREAD_CREATE_DETACHED);// 设置为分离状态线程退出后自动释放资源// 3. 计算并设置线程栈大小stack_sizeos::Linux::default_stack_size(thr_type);if(stack_size0){pthread_attr_setstacksize(attr,stack_size);}// 4. 关键调用 glibc 的 pthread_create 创建线程// tid: 接收创建成功的 posix 线程 id// attr: 线程属性栈大小、分离状态等// java_start: C 层的启动哨兵函数负责引导并同步线程状态// thread: 将当前的 JavaThread 对象指针作为参数传给 java_startpthread_t tid;intretpthread_create(tid,attr,java_start,thread);// 5. 收尾工作与状态同步pthread_attr_destroy(attr);if(ret!0){// 倘若 pthread_create 返回非0说明操作系统拒绝创建新线程如超过 max user processesthread-set_osthread(NULL);deleteosthread;returnfalse;}// 保存 posix 线程 ID 到 OSThreadosthread-set_pthread_id(tid);returntrue;}五、 线程启动与回调生命周期通过pthread_create创建的线程并不会立即疯狂执行 Java 代码它需要经过 JVM 的初始化同步。1. 哨兵启动函数java_start(os_linux.cpp)新诞生在内核之中的线程其执行的第一个 C 函数就是java_start。staticvoid*java_start(Thread*thread){// 1. 将当前底层线程的 pid (Process ID, Linux 下即轻量级进程的 TID) 存入 OSThreadOSThread*osthreadthread-osthread();osthread-set_thread_id(os::current_thread_id());// 2. 初始化底层 OS 信号处理比如用于处理内存断言、处理线程中断信号等os::Linux::hotspot_sigmask(thread);os::Linux::init_thread_fpu_state();{// 3. 线程状态同步控制// 此时新线程已经就绪但必须挂起等待直到父线程调用 start() 的线程明确发出指令MutexLockerExml(osthread-startThread_lock(),Mutex::_no_safepoint_check_flag);// 将状态从 INITIALIZED 变更为 RUNNABLEosthread-set_state(INITIALIZED);// 通知父线程我已经初始化完毕了osthread-startThread_lock()-notify_all();// 循环等待直到父线程在 Thread::start 里面将状态改写成更为高级的状态或发出信号while(osthread-get_state()INITIALIZED){osthread-startThread_lock()-wait(Mutex::_no_safepoint_check_flag);}}// 4. 真正破茧而出调用 JavaThread 的 run 方法thread-run();return0;}2. 父线程的放行信号Thread::start(thread.cpp)回到父线程的视角。在jvm.cpp中最后一步调用了Thread::start(native_thread)这里才是激活子线程的开关。voidThread::start(Thread*thread){trace(start,thread);// 获取子线程的 startThread_lockOSThread*osthreadthread-osthread();if(osthread!NULL){MutexLockerExml(osthread-startThread_lock(),Mutex::_no_safepoint_check_flag);// 将子线程状态由 INITIALIZED 变更为 RUNNABLE// 这打破了 java_start 中 while(get_state() INITIALIZED) 的死循环if(osthread-get_state()INITIALIZED){osthread-set_state(RUNNABLE);// 唤醒在 startThread_lock 上等待的子线程osthread-startThread_lock()-notify_all();}}}3. 回调 Java 层run()方法thread.cpp被唤醒的子线程从java_start继续向下执行进入thread-run()-JavaThread::run()。voidJavaThread::run(){// 初始化 TLAB (Thread Local Allocation Buffer)为 Java 对象分配内存做准备this-initialize_tlab();// 设置安全点Safepoint相关的线程状态this-set_thread_state(_thread_in_vm);// 进入核心的内部执行器thread_main_inner();}voidJavaThread::thread_main_inner(){if(!this-has_pending_exception()_entry_point!NULL){// 创建一个资源上下文ResourceMarkrm(this);HandleMarkhm(this);// 执行 _entry_point。这个 entry_point 在 jvm.cpp 中指向的是 thread_entry// thread_entry 会通过 JavaCalls::call_virtual 调用 Java 类中的 Thread.run() 方法_entry_point(this,this);}// 线程执行完毕后的清理工作如退出 Safepoint移出线程组销毁 JavaThreadthis-exit(false);deletethis;}六、 Linux 内核与系统调用底层为了形成闭环必须理解pthread_create在 Linux 系统层面的真实动作。在glibc源代码中pthread_create最终会填充一个包含了各种控制标志的结构体并调用封装好的宏来执行内核系统调用。其本质是调用了clone()。Linux 内核中的clone标志位分析在 Linux 2.6 及以后的 NPTLNative POSIX Thread Library架构下线程和进程在内核中都由task_struct结构体表示。创建线程时传递给clone系统调用的关键参数如下intclone(int(*fn)(void*),void*child_stack,intflags,void*arg,...);其底层传递的flags掩码通常包含标志位 (Flags)作用说明为什么线程需要CLONE_VM共享进程虚拟内存空间保证所有 Java 线程能够看到同一个堆Heap和方法区。CLONE_FS共享文件系统信息共享当前工作目录cwd以及根目录、umask 权限。CLONE_FILES共享文件描述符表FD Table任何一个线程打开的 Socket 或 File其他线程都能直接读写。CLONE_SIGHAND共享信号处理函数表JVM 注册的信号处理器如挂起、内存屏障能对所有线程生效。CLONE_THREAD放入相同的线程组TGID使得新创建的 LWP 在系统层面的getpid()返回的是同一个进程ID。总结全链路图表整个过程可以通过一个高效的映射表来闭环复盘阶段所在源文件 / 库关键函数核心职责1. 触发Thread.javastart()-start0()用户入口面向 Java 的状态检查2. 桥接Thread.cJVM_StartThreadJNI 动态绑定映射3. 分配jvm.cppnew JavaThread()分配 JVM 层 C 线程对象与初始化校验4. 派发thread.cppos::create_thread()屏蔽操作系统差异向 OS 适配层派发请求5. 系统调用os_linux.cpppthread_create()分配 OSThread调用 POSIX 线程库6. 内核创建Glibc/Kernelclone(CLONE_VM | ...)内核真正创建 LWP 并分配独立的task_struct7. 握手os_linux.cppjava_start()子线程上线通过 Monitor 挂起等待父线程信号8. 回调thread.cpp/jvm.cppJavaCalls::call_virtual握手解锁后反向回调 Java 对象的run()方法这套一比一映射的模型保证了 Java 线程能够享受到 Linux 内核带来的原生调度优化如 CFS 调度器但也意味着在 Java 中频繁创建和销毁线程的系统调用开销极高。这也就是为什么在企业级高并发系统工程师的武器库中线程池ThreadPoolExecutor永远是标配的原因。