Java线程状态切换机制剖析
线程状态切换机制
前言
本文旨在记录近期研读Java源码的学习心得与疑难问题。由于个人理解水平有限,文中内容难免存在疏漏,恳请读者不吝指正。
线程状态切换机制剖析
在 Java 虚拟机(HotSpot)的架构中,Java 语言层面的线程状态(java.lang.Thread.State)与 JVM 内部实现的状态(JavaThreadState)并非一一对应。Java 线程的状态切换不仅涉及 JVM 内部状态机的流转,还深度依赖操作系统的底层同步原语(如 Pthreads、互斥锁、条件变量)以及 HotSpot 的安全点(Safepoint)机制。
作为程序员,理解 Java 线程状态切换的核心在于明白:Java 层的 java.lang.Thread.State 只体现了一个高层的抽象状态,而 JVM 内部(HotSpot)有一套更复杂的 JavaThreadState,它们最终都依赖操作系统的 OS 线程状态(如 Linux 的 TASK_RUNNING、TASK_INTERRUPTIBLE)来实现。
在 OpenJDK 8中,Java 线程采用的是 1:1 模型,即一个 Java 线程对应一个 JVM 内部的 JavaThread,并对应一个操作系统的原生线程(Native Thread)。
以下将结合 OpenJDK 8源码,从线程创建、就绪、阻塞、等待到销毁的核心生命周期,深度剖析其状态切换机制。
一、 线程状态映射:Java 状态 vs JVM 内部状态
在阅读源码前,需明确两种状态枚举的对应关系:
- Java 层状态 (
java.lang.Thread.State):NEW,RUNNABLE,BLOCKED,WAITING,TIMED_WAITING,TERMINATED。 - JVM 内部状态 (
src/share/vm/utilities/globalDefinitions.hpp):
enum JavaThreadState {
_thread_uninitialized = 0, // 线程正在创建
_thread_new = 2, // 已创建但尚未启动
_thread_in_native = 4, // 正在执行本地代码(JNI)
_thread_in_vm = 6, // 正在 JVM 内部执行(如 GC 准备、类加载)
_thread_in_Java = 8, // 正在执行解释或编译的 Java 字节码
_thread_blocked = 10, // 阻塞状态(等待锁、条件变量、或安全点 Safepoint 挂起)
...
};
二、 核心状态切换源码分析
1. NEW → \rightarrow → RUNNABLE:线程的创建与启动
当在 Java 中调用 thread.start() 时,本地方法 JVM_StartThread 会被触发。这一步完成了从 NEW 到 JVM 内部 _thread_new 再到 OS 线程拉起的全过程。
源码剖析一:src/share/vm/prims/jvm.cpp (JVM_StartThread)
JVM_ENTRY(void, JVM_StartThread(JNIEnv* env, jobject jthread))
JavaThread *native_thread = NULL;
// 1. 检查线程是否已经启动过,防止重复启动
if (java_lang_Thread::thread(JNIHandles::resolve_non_null(jthread)) != NULL) {
throw_illegal_thread_state = true;
}
if (!throw_illegal_thread_state) {
// 获取期望的栈大小
jlong size = java_lang_Thread::stackSize(JNIHandles::resolve_non_null(jthread));
// 2. 创建 JVM 内部的 JavaThread 对象
// thread_entry 是线程启动后真正执行的高层回调函数(执行 run 方法)
native_thread = new JavaThread(&thread_entry, size);
if (native_thread->osthread() != NULL) {
// 3. 此时线程已成功与 OS 线程绑定,设置其内部状态为 _thread_new
native_thread->prepare(jthread);
}
}
// ... 省略部分校验逻辑 ...
// 4. 真正唤醒并运行操作系统的原生线程
Thread::start(native_thread);
JVM_END
源码剖析二:src/os/linux/vm/os_linux.cpp (os::create_thread)
在 new JavaThread() 内部,会调用具体操作系统的创建方法。在 Linux 下通过 pthread_create 实现:
bool os::create_thread(Thread* thread, ThreadType thr_type, size_t stack_size) {
// 1. 分配 OSThread 结构体,用于记录底层 OS 线程句柄和状态
OSThread* osthread = new OSThread(NULL, NULL);
thread->set_osthread(osthread);
// 初始化底层线程状态为 ALLOCATED(已分配)
osthread->set_state(ALLOCATED);
pthread_t tid;
// 2. 调用 Linux 的 pthread_create 创建底层原生线程
// java_start 是底层的统一线程启动入口
int ret = pthread_create(&tid, &attr, java_start, thread);
if (ret != 0) {
// 创建失败处理...
return false;
}
// 保存系统级线程 ID (TID)
osthread->set_pthread_id(tid);
return true;
}
源码剖析三:src/share/vm/runtime/thread.cpp (Thread::start)
此时底层的 pthread 已经创建,但处于同步等待状态(通过信号量或互斥量挂起),直到 Thread::start 被调用:
void Thread::start(Thread* thread) {
trace_thread_start(thread, true);
// 获取 OSThread 指针
OSThread* osthread = thread->osthread();
// 更改底层 OS 线程状态为 INITIALIZED
osthread->set_state(INITIALIZED);
// 核心:唤醒在 java_start 中因为等待同步信号而挂起的底层线程
// 内部通常使用 os::PlatformEvent->unpark() 或 sem_post
os::start_thread(thread);
}
系统工程师视角: Java 线程的
RUNNABLE状态在 JVM 层面细分为_thread_in_Java(正在执行字节码)和_thread_in_native(执行 JNI)。在 Linux OS 层面,无论线程是在计算还是在等待 CPU 时间片,其底层状态均是TASK_RUNNING。
2. RUNNABLE ↔ \leftrightarrow ↔ BLOCKED:synchronized 锁竞争
当 Java 线程试图进入 synchronized 块且锁已被占用时,线程状态转换为 BLOCKED。这涉及到 HotSpot 的重量级锁实现 ObjectMonitor。
源码剖析四:src/share/vm/runtime/objectMonitor.cpp (ObjectMonitor::EnterI)
当轻量级锁膨胀为重量级锁,或者直接竞争失败时,线程进入 EnterI 方法:
void ATTR ObjectMonitor::EnterI (TRAPS) {
Thread * Self = THREAD ;
assert (Self->is_Java_thread(), "invariant") ;
JavaThread * jt = (JavaThread *) Self ;
// 1. 尝试再次构造一次轻量级的自旋竞争锁,减少挂起代价
if (TryLock (Self) > 0) { return; }
// 2. 将当前线程包装成 ObjectWaiter 节点
ObjectWaiter node(Self) ;
Self->_ParkEvent->Reset() ;
node.TState = ObjectWaiter::TS_CXQ ;
// 3. 将节点原子地(CAS)推入锁的竞争队列 _cxq
ObjectWaiter * nxt ;
for (;;) {
node._next = nxt = _cxq ;
if (Atomic::cmpxchg_ptr (&node, &_cxq, nxt) == nxt) break ;
if (TryLock (Self) > 0) { return; } // 双重检查
}
// 4. 【状态切换核心】改变 JVM 内部状态为 _thread_blocked
// 这会让安全点(Safepoint)机制感知到该线程已阻塞,不需要等待它
ThreadBlockInVM tbivm(jt);
// 5. 进入操作系统级别的挂起循环
for (;;) {
if (TryLock (Self) > 0) break ; // 再次尝试获取锁
// 检查是否有中断请求
if (Self->is_interrupted(true)) {
// 中断逻辑处理...
}
// 6. 调用操作系统底层的 park() 挂起线程
// 在 Linux 上,底层是通过 pthread_cond_wait 或 OS 信号量实现,进入 TASK_UNINTERRUPTIBLE/TASK_INTERRUPTIBLE 状态
Self->_ParkEvent->park() ;
// 被唤醒(Unpark)后,继续循环尝试 TryLock
// 只有拿到锁,才能跳出循环,退出 ThreadBlockInVM 作用域,恢复 _thread_in_Java 状态
}
}
3. RUNNABLE ↔ \leftrightarrow ↔ WAITING / TIMED_WAITING:显式挂起
Java 显式挂起主要通过两种途径:Object.wait()(配合重量级锁)和 LockSupport.park()(配合 JUC 锁)。
途径 A:Object.wait() 机制
源码剖析五:src/share/vm/runtime/objectMonitor.cpp (ObjectMonitor::wait)
void ObjectMonitor::wait(jlong millis, bool interruptible, TRAPS) {
Thread * Self = THREAD ;
JavaThread * jt = (JavaThread *) Self ;
// 1. 构造 ObjectWaiter 节点,状态置为 TS_WAIT (等待队列)
ObjectWaiter node(Self);
node.TState = ObjectWaiter::TS_WAIT ;
// 2. 释放当前持有的重量级锁(因为 wait 必须在 synchronized 内调用)
int revocation_count = 0;
__waiters++;
AddWaiter (&node) ; // 假如到 _WaitSet 队列中
Exit (true, Self) ; // 释放锁,允许其他 EnterI 的线程进来
// 3. 核心:转换 JVM 线程状态为阻塞/等待
// 注意:若传入的 millis > 0,Java 层表现为 TIMED_WAITING,否则为 WAITING
// 但在 JVM 内部统一使用 ThreadBlockInVM 封装
ThreadBlockInVM tbivm(jt);
if (millis == 0) {
// 永久等待:调用底层操作系统的 park()
Self->_ParkEvent->park() ;
} else {
// 限时等待:调用底层操作系统的 timed_park()
Self->_ParkEvent->park(millis) ;
}
// 4. 线程被 notify() 唤醒或者超时醒来后,必须重新去竞争锁
// 此时状态其实又退回到了类似同步块竞争的逻辑
ObjectMonitor::EnterI (THREAD) ;
}
途径 B:LockSupport.park() 机制
j.u.c 包下的核心 AQS 依赖 Unsafe.park。它不依赖 ObjectMonitor,而是直接操作线程的 Parker 对象。
源码剖析六:src/os/posix/vm/os_posix.cpp (Parker::park)
以 POSIX(Linux/Unix)系统为例,分析底层的系统级原语调用:
void Parker::park(bool isAbsolute, jlong time) {
// 1. 如果此前已经有 unpark() 产生的许可(counter > 0),直接消耗掉许可并返回
if (Atomic::xchg(0, &_counter) > 0) return;
Thread* thread = Thread::current();
JavaThread *jt = (JavaThread *)thread;
// 如果线程已被中断,直接返回
if (jt->is_interrupted(false)) return;
// 2. 计算超时时间
struct timespec absTime;
if (time > 0) {
compute_abstime(&absTime, isAbsolute, time);
}
// 3. 变更 JVM 线程状态
ThreadBlockInVM tbivm(jt);
// 4. 进入底层互斥锁和条件变量的等待
pthread_mutex_lock(_mutex);
if (_counter > 0) { // 双重检查许可
_counter = 0;
pthread_mutex_unlock(_mutex);
return;
}
// 5. 执行 OS 级挂起
if (time == 0) {
// 对应 Java 的 WAITING 状态
// 调用 Linux 原生 POSIX 线程库,线程进入睡眠状态
pthread_cond_wait(_cond, _mutex);
} else {
// 对应 Java 的 TIMED_WAITING 状态
pthread_cond_timedwait(_cond, _mutex, &absTime);
}
// 被唤醒后,重置许可
_counter = 0;
pthread_mutex_unlock(_mutex);
}
4. TERMINATED:线程的销毁与资源回收
当 Java 的 run() 方法执行完毕或者抛出未捕获异常退出时,线程开始走向 TERMINATED 状态。
源码剖析七:src/share/vm/runtime/thread.cpp (JavaThread::exit)
void JavaThread::exit(bool destroy_vm, ExitType exit_type) {
// 1. 触发 Java 层的 Thread.exit() 方法,清理 ThreadLocal 等资源
if (!this->has_pending_exception() &&
juint(chunks) == 0 &&
java_lang_Thread::thread(threadObj()) == this) {
// 通过虚拟机内部调用机制执行 java.lang.Thread.exit()
JavaValue result(T_VOID);
JavaCalls::call_virtual(&result, threadObj, ...);
}
// 2. 通知 JVM 的线程服务监视器(ThreadService),此处会将高层状态变更为 TERMINATED
ThreadService::current_thread_status_changed(this, java_lang_Thread::TERMINATED);
// 3. 移除锁资源、释放 JNI 句柄块
assert(!has_last_Java_frame(), "this must be remove");
// 4. 从全局线程列表中移除当前线程(需要获取 Threads_lock 锁)
Threads::remove(this);
// 5. 彻底释放底层 OSThread
this->set_osthread(NULL);
// 6. 调用底层操作系统退出原语(如 pthread_exit),OS 线程生命周期终结
os::free_thread(osthread);
}
三、 系统级视角:总结与映射表
从系统工程师的角度来看,Java 线程状态的切换是一个典型的分层抽象与状态映射设计。
Java 线程状态 (Thread.State) |
JVM 内部状态 (JavaThreadState) |
底层 OS 线程状态 (以 Linux 为例) | 触发的关键源码/原语 |
|---|---|---|---|
| NEW | _thread_uninitialized |
无 / ALLOCATED |
new Thread(), 内部调用 pthread_create |
| RUNNABLE | _thread_in_Java / _thread_in_native |
TASK_RUNNING (正在运行或在就绪队列中) |
Thread.start() → \rightarrow → os::start_thread |
| BLOCKED | _thread_blocked |
TASK_INTERRUPTIBLE / TASK_UNINTERRUPTIBLE |
ObjectMonitor::EnterI → \rightarrow → PlatformEvent::park() |
| WAITING | _thread_blocked |
TASK_INTERRUPTIBLE |
ObjectMonitor::wait(0) 或 Parker::park(0) → \rightarrow → pthread_cond_wait |
| TIMED_WAITING | _thread_blocked |
TASK_INTERRUPTIBLE |
Parker::park(time) → \rightarrow → pthread_cond_timedwait |
| TERMINATED | _thread_uninitialized (已被移出列表) |
EXIT_ZOMBIE / 销毁 |
JavaThread::exit → \rightarrow → pthread_exit |
核心机制沉淀:
ThreadBlockInVM的妙用:在任何涉及线程可能挂起的操作(如等锁、等条件变量)前,JVM 都会显式声明一个ThreadBlockInVM作用域。这个类的构造函数会把当前线程的状态改为_thread_blocked。这样,当垃圾回收器(GC)发起安全点(Safepoint)同步时,看到该线程已经是_thread_blocked状态,就知道它绝对不会修改 Java 堆和寄存器,从而无需等待该线程清醒即可直接进入 GC 阶段。这是一种极致的并发异步优化。- 1:1 模型的性能损耗:从
RUNNABLE转换为BLOCKED/WAITING,其底层不可避免地调用了pthread_cond_wait等 POSIX 原语,这触发了严重的从用户态到内核态的上下文切换(Context Switch),涉及到 CPU 寄存器恢复、页表缓存(TLB)部分失效等,这也是重量级锁以及传统 Java 线程切换开销大的根本原因。
openEuler 是由开放原子开源基金会孵化的全场景开源操作系统项目,面向数字基础设施四大核心场景(服务器、云计算、边缘计算、嵌入式),全面支持 ARM、x86、RISC-V、loongArch、PowerPC、SW-64 等多样性计算架构
更多推荐


所有评论(0)