Java 线程模型
1.线程模型
- 用户态线程 (User-Level Thread)
jdk 1.0 的早期实现,完全由 JVM 在用户空间管理
- 轻量级:创建和销毁成本低,线程切换、调度开销小、速度快
- 不可见的阻塞:一个用户线程阻塞会导致整个进程阻塞(内核视角只有单线程)
- 内核态线程 (Kernel-Level Thread)
Java 1.2 引入,HotSpot 采用此模型,Java 线程 1:1 映射到内核线程,由操作系统内核直接管理
- 性能:线程切换、调度需要内核参与,涉及上下文切换,开销大
- 并行性:多核 CPU 可真正并行执行
- 混合模型 (协程)
jdk 21 提供虚拟线程(Virtual Threads),本质上是协程的一种实现
- 本质:大量的用户态线程(虚拟线程)映射到少量的载体线程(
ForkJoinPool
内核线程) - 优势:兼容现有
Thread
API,无需关注协程底层即可适应高并发场景,避免上下文切换开销 - 场景:适合 I/O 密集型场景,例如网络服务、数据库访问等
2. JVM 创建 Native Thread 的流程
当调用 new Thread().start()
时:
- JAVA 创建一个
Thread
对象,初始化栈大小、优先级等参数 - JVM 内部检查线程参数 (如
-Xss
栈大小),调用pthread_create
(Linux) 或CreateThread
(Windows) 等操作系统 API 创建线程 - 新创建的 Native Thread 执行入口函数 (如
hotsopt/src/os/linux/vm/os_linux.cpp
中的java_start
函数), 初始化线程的 栈帧、程序计数器、寄存器状态,并关联 Java 层的Thread
对象 - Native Thread 跳转到 Java
Thread
的run()
方法的机器码入口,开始执行用户代码
3. Java 线程与 Native Thread 的映射细节
线程本地存储
- 通过
ThraedLocal
存储的变量,保存在 JavaThread
对象内部(通过ThreadLocalMap
维护) - Native Thread 的 TLS 中存储了 JVM 内部线程结构指针,用于快速定位 Java 层状态
线程中断
- 当调用
Thread.interrupt()
并不会直接操作 Native Thread,而是设置Thread
对象的中断状态位 - 如果 Native Thread 因
wait()
、sleep()
等阻塞时,JVM 注入中断信号(如 Linux 的SIGINT
)唤醒线程,抛出InterruptedException
JNI线程附加
- 当 Native 代码通过 JNI 调用
AttachCurrentThread()
, JVM 会将当前 Native Thread 绑定到新的 JavaThread
对象 - 该线程会被 JVM 视为活动的 Java 线程,直到显示调用
DetachCurrentThread()
4. 性能与限制
- 每个 Native Thread 需要独立的内核栈 (通常 1~8 MB)和上下文切换成本
- Native Thread 数量受操作系统的线程数上限限制
虚拟线程
JDK19+ 引入了虚拟线程,多由 JVM 管理,多个虚拟线程复用一个 Native Thread,创建和调度开销都很小