Thread / Runnable 多线程核心源码
概述
java.lang.Thread 和 java.lang.Runnable 是 Java 多线程编程的基石。Runnable 定义任务接口(一个 run() 方法),Thread 封装操作系统线程并提供线程控制 API(start() / sleep() / join() / interrupt() 等)。两者通过 模板方法模式 结合:Thread 本身也实现了 Runnable,Thread.run() 会调用传入的 Runnable 实例的 run()。
深入理解 Thread 的源码实现,对于掌握 Java 并发模型、诊断多线程问题、以及编写高性能并发代码至关重要。
本文基于 OpenJDK 21 源码分析,关键实现会对比不同操作系统(Linux / Windows)的行为差异。
1. Thread 的内部状态
1.1 threadStatus 字段
Thread 类使用 private volatile int threadStatus 字段追踪线程的生命周期状态:
// java.lang.Thread(部分源码)
public class Thread implements Runnable {
// 线程状态,volatile 保证多线程可见性
private volatile int threadStatus = 0;
// 线程优先级
private int priority;
// 是否为守护线程
private boolean daemon = false;
// 线程名
private volatile String name;
}threadStatus 为 0 表示线程处于 NEW 状态(尚未启动)。
1.2 Thread.State 枚举
JDK 定义了 Thread.State 枚举,将 JVM 线程状态映射为 6 个高层状态:
// java.lang.Thread.State
public enum State {
NEW, // 尚未 start()
RUNNABLE, // 正在 JVM 中执行,或等待 OS 调度
BLOCKED, // 等待监视器锁(synchronized 阻塞)
WAITING, // 无限期等待(wait / join / park)
TIMED_WAITING,// 有超时等待(sleep / wait(timeout) / join(timeout) / parkNanos)
TERMINATED // 执行完毕
}1.3 JVM 线程状态与 Java 状态的映射
Thread.getState() 的实现并非单纯读取 threadStatus 字段,而是通过 native 方法向 JVM 查询当前状态:
// java.lang.Thread
public State getState() {
// 如果线程已启动但未终止,向 JVM 查询当前状态
return jvmThreadState(threadStatus);
}
// 非公开的 native 方法,从 JVM 获取线程状态
// 实际调用 JVM_GetThreadState 这个 JVM 函数JVM 端(HotSpot)的 JVM_GetThreadState 将 JVM 内部状态映射为 Java 枚举:
// hotspot/share/prims/jvm.cpp
JVM_ENTRY(jint, JVM_GetThreadState(JNIEnv* env, jthread jthread)) {
JavaThread* java_thread = java_lang_thread::thread(jthread);
// 获取 JVM 内部的 JavaThread 状态
java_thread->thread_state();
// 映射: JVM 状态 → Java Thread.State
// _thread_new → NEW
// _thread_in_Java → RUNNABLE
// _thread_blocked → BLOCKED
// _thread_in_native → RUNNABLE(正在执行 native 代码)
// ...
}关键差异:
| JVM 内部状态 | Java Thread.State | 含义 |
|---|---|---|
_thread_new | NEW | 线程对象创建,尚未 start |
_thread_in_Java | RUNNABLE | 执行 Java 字节码 |
_thread_in_native | RUNNABLE | 执行 native 方法 |
_thread_blocked | BLOCKED | 等待监视器锁 |
_thread_suspend | WAITING | 被调用了 suspend()(已废弃) |
值得注意的是:JVM 的 _thread_in_native(执行 native 代码)也被映射为 RUNNABLE,因为从 Java 层面看线程确实是"可运行"的。
1.4 threadStatus 的写入时机
// java.lang.Thread.start()
public synchronized void start() {
if (threadStatus != 0)
throw new IllegalThreadStateException();
group.add(this); // 加入 ThreadGroup
start0(); // 启动 native 线程
// start0() 成功后 JVM 回调会将 threadStatus 设为非 0
}threadStatus 的转换由 JVM 在以下时机自动更新:
start0()成功后 → 变为非 NEW- 线程
run()方法执行完毕 → 变为TERMINATED
2. Thread.start() 的 native 启动
2.1 Java 层调用链
// java.lang.Thread
public synchronized void start() {
// 1. 检查线程状态(防止重复启动)
if (threadStatus != 0)
throw new IllegalThreadStateException();
// 2. 通知 ThreadGroup 有新线程加入
group.add(this);
// 3. 标记已启动(避免 group.add 失败后的状态不一致)
started = false;
try {
// 4. 调用 native 方法,真正的 OS 线程创建
start0();
started = true;
} finally {
try {
if (!started) {
group.threadStartFailed(this);
}
} catch (Throwable ignore) {
// group.threadStartFailed 不应抛出异常
}
}
}
// native 方法声明
private native void start0();两次 start() 检查:
threadStatus != 0抛IllegalThreadStateException- 一旦
start0()调用成功,threadStatus被 JVM 设为非 0 - 因此同一个线程不能调用两次
start()
2.2 JVM 层:JVM_StartThread()
// hotspot/share/prims/jvm.cpp
JVM_ENTRY(void, JVM_StartThread(JNIEnv* env, jobject jthread)) {
JavaThread* native_thread = NULL;
// 创建 JavaThread 对象(此时尚未创建 OS 线程)
native_thread = new JavaThread(&thread_entry, sz);
// 将 JavaThread 与 Java 层的 Thread 对象关联
java_lang_thread::set_thread(jthread(), native_thread);
java_lang_thread::set_priority(jthread(), native_thread->priority());
java_lang_thread::set_daemon(jthread(), native_thread->is_daemon());
}2.3 创建 OS 线程
// hotspot/share/runtime/thread.cpp
JavaThread::JavaThread(ThreadFunction entry_point, size_t stack_sz) :
OSThread() // 先创建 OS 线程抽象
{
// 创建 OS 级线程
os::create_thread(this, entry_point);
}
// Linux 实现:pthread_create
// hotspot/os/linux/os_linux.cpp
bool os::create_thread(JavaThread* thread, ThreadFunction func) {
pthread_t tid;
pthread_attr_t attr;
pthread_attr_init(&attr);
pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
// 创建 pthread 线程,入口为 java_start
int ret = pthread_create(&tid, &attr, (void* (*)(void*)) java_start, thread);
return (ret == 0);
}
// Windows 实现:CreateThread
// hotspot/os/windows/os_windows.cpp
bool os::create_thread(JavaThread* thread, ThreadFunction func) {
HANDLE handle = CreateThread(
NULL, // 安全属性
thread->stack_size(), // 栈大小
(unsigned __stdcall*) java_start, // 线程入口
thread, // 参数
0, // 创建标志
&thread->osthread()->thread_id() // 线程 ID
);
return (handle != NULL);
}2.4 thread_entry 回调
// hotspot/share/runtime/thread.cpp
static void thread_entry(JavaThread* thread, TRAPS) {
// 获取 Java 层的 Thread 对象
HandleMark hm(THREAD);
Klass* k = SystemDictionary::Thread_klass();
JavaValue result(T_VOID);
// 通过 Java 反射调用 Thread.run()
JavaCalls::call_virtual(&result,
thread->threadObj(), // Java Thread 对象
k,
vmSymbols::run_method_name(), // "run"
vmSymbols::void_method_signature(),
THREAD);
}启动流程总结:
Thread.start() (Java)
│
▼
start0() native ──────────────────┐
│ │
▼ ▼
JVM_StartThread() 检查 threadStatus != 0
│ → 抛 IllegalThreadStateException
▼
new JavaThread(&thread_entry)
│
├── Linux: pthread_create(..., java_start)
└── Windows: CreateThread(..., java_start)
│
▼
java_start() (新 OS 线程)
│
▼
thread_entry() → 调用 Thread.run()3. Thread.sleep(long) 的 native 实现
3.1 Java 层调用链
// java.lang.Thread
public static void sleep(long millis) throws InterruptedException {
sleep(millis, 0); // 委托给重载方法
}
public static void sleep(long millis, int nanos) throws InterruptedException {
if (millis < 0) {
throw new IllegalArgumentException("timeout value is negative");
}
if (nanos < 0 || nanos > 999999) {
throw new IllegalArgumentException("nanosecond timeout value out of range");
}
if (nanos >= 500000 || (nanos != 0 && millis == 0)) {
millis++; // 纳秒四舍五入到毫秒
}
sleep0(millis); // native 方法
}
private static native void sleep0(long millis) throws InterruptedException;注意 nanos 的处理:JDK 中的纳秒参数只是一个近似值,底层操作系统通常只保证毫秒精度。
3.2 Linux 实现:pthread_cond_timedwait
// hotspot/os/linux/os_linux.cpp
void os::sleep(JavaThread* thread, jlong millis, bool interruptible) {
// 使用条件变量实现定时等待
Monitor* mtx = thread->currentParkMonitor();
// 没有条件变量时的降级实现
if (mtx == NULL) {
// 使用 pthread_cond_timedwait
pthread_mutex_lock(&mutex);
struct timespec abst;
compute_abstime(&abst, millis); // 计算绝对超时时间
while (/* 线程未被中断 */) {
int ret = pthread_cond_timedwait(&cond, &mutex, &abst);
if (ret == ETIMEDOUT) break; // 超时,正常返回
if (ret == 0) continue; // 被条件变量唤醒(可能是虚假唤醒)
}
pthread_mutex_unlock(&mutex);
}
}关键实现细节:
- 使用
CLOCK_MONOTONIC时间源(不受系统时间调整影响) pthread_cond_timedwait返回ETIMEDOUT表示超时- 返回 0 表示被条件变量唤醒(需再次检查条件)
3.3 Windows 实现:WaitForSingleObject
// hotspot/os/windows/os_windows.cpp
void os::sleep(JavaThread* thread, jlong millis, bool interruptible) {
HANDLE interrupt_event = thread->osthread()->interrupt_event();
if (interruptible) {
// 等待中断事件或超时
DWORD ret = WaitForSingleObject(interrupt_event, (DWORD)millis);
if (ret == WAIT_OBJECT_0) {
// 被中断事件唤醒 → 抛出 InterruptedException
// ...
}
// WAIT_TIMEOUT → 正常超时返回
} else {
// 不可中断的 sleep,直接 Sleep()
Sleep((DWORD)millis);
}
}WaitForSingleObject 是 Windows 内核级别的等待原语,可以同时等待事件信号和超时。
3.4 中断响应
// 线程阻塞在 sleep() 时收到中断信号的处理
// hotspot/share/runtime/thread.cpp
void JavaThread::interrupt(JavaThread* thread) {
// 1. 设置 JVM 级中断标志
thread->set_interrupted(true);
// 2. 如果线程正在 sleep/park 中,需要唤醒它
// Linux: pthread_cond_signal / unpark
// Windows: SetEvent(interrupt_event)
if (thread->is_sleeping()) {
thread->unpark(); // 唤醒线程
}
}Java 层的中断响应:
// Thread.sleep() 收到中断时的行为
// 被中断 → 清除中断标志 → 抛 InterruptedException
public static void sleep(long millis) throws InterruptedException {
// JVM 实现会检查中断标志:
// if (Thread.interrupted()) { // 注意:interrupted() 会清除标志
// throw new InterruptedException();
// }
}总结:sleep() 期间收到中断 → JVM 唤醒线程 → 清除中断标志 → 抛出 InterruptedException。
4. Thread.join() 的 wait(0) 实现
4.1 核心实现
// java.lang.Thread
// 等待线程终止
public final void join() throws InterruptedException {
join(0); // 0 表示永久等待
}
// 带超时的 join
public final synchronized void join(long millis) throws InterruptedException {
long base = System.currentTimeMillis();
long now = 0;
if (millis < 0) {
throw new IllegalArgumentException("timeout value is negative");
}
if (millis == 0) {
// 永久等待:isAlive() 返回 false 之前一直等待
while (isAlive()) {
wait(0L); // wait(0) = 永久等待
}
} else {
// 超时等待:每次被唤醒后检查剩余时间
while (isAlive()) {
long delay = millis - now;
if (delay <= 0) {
break; // 超时,不再等待
}
wait(delay);
now = System.currentTimeMillis() - base;
}
}
}4.2 join(0) 的语义
join(0) 的关键是 wait(0L)——Object.wait(0) 的含义是永久等待,直到被 notifyAll() 唤醒。
为什么可以在 Thread 对象上调用 wait()?
Thread t = new Thread(() -> { ... });
t.start(); // t 已启动
t.join(); // 当前线程对 t 对象调用 wait(0)
// 需要先持有 t 的锁(join() 是 synchronized 方法)join() 被声明为 synchronized,因此当前线程获取了目标线程对象 t 的监视器锁,然后可以在 t 上调用 wait()。
4.3 JVM 自动 notifyAll()
线程终止时,JVM 会调用 notifyAll() 唤醒所有在等待该线程结束的线程:
// hotspot/share/runtime/thread.cpp
void JavaThread::exit(bool destroy_vm, ExitType exit_type) {
// ... 清理资源 ...
// 通知所有等待该线程结束的线程
// 这等价于在 Java 层调用 this.notifyAll()
ensure_join(this);
}
static void ensure_join(JavaThread* thread) {
// 获取 Java 层 Thread 对象的监视器锁
Handle threadObj(thread, thread->threadObj());
ObjectSynchronizer::jni_enter(threadObj, THREAD);
// 设置线程状态为 TERMINATED
java_lang_Thread::set_thread_status(threadObj(),
java_lang_Thread::TERMINATED);
// 清除线程对象关联的 native 线程
java_lang_Thread::set_thread(threadObj(), NULL);
// 唤醒在 threadObj 上 wait() 的所有线程 → join() 返回
ObjectSynchronizer::notifyall(threadObj(), THREAD);
ObjectSynchronizer::jni_exit(threadObj(), THREAD);
}4.4 等待链
main thread worker thread
│ │
│ worker.join() │
│ (synchronized worker) │
│ │ │
│ while (isAlive()) │
│ │ │
│ worker.wait(0) │
│ (进入 Waiting 状态) │
│ │ │ run() 执行完毕
│ │ │
│ │◄──── exit() ────────────────┤
│ │ │ │
│ │ notifyAll() │
│ │ │ │
│ wait() 返回 │
│ isAlive() == false │
│ join() 正常返回 │
│ │5. Thread.interrupt() 的 3 种响应
5.1 Java 层调用链
// java.lang.Thread
public void interrupt() {
if (this != Thread.currentThread()) {
checkAccess(); // 安全检查
}
synchronized (blockerLock) {
Interruptible b = blocker;
if (b != null) {
interrupt0(); // 先设置 JVM 中断标志
b.interrupt(this); // 通知可中断的 I/O 操作
return;
}
}
interrupt0(); // native: 设置 JVM 级中断标志
}
private native void interrupt0();5.2 JVM 层:设置中断标志
// hotspot/share/runtime/thread.cpp
void JavaThread::interrupt(JavaThread* thread) {
// 1. 设置 JVM 级中断标志
thread->set_interrupted(true);
// 2. 如果线程在 park/sleep 中,唤醒它
// Linux: unpark()
// Windows: SetEvent(interrupt_event)
thread->unpark();
}5.3 三种中断响应机制
响应 1:可中断阻塞(sleep() / wait() / join())
这是最常见的中断响应。线程在 sleep()、wait()、join() 阻塞时收到中断信号:
// 响应流程:
// 1. JVM 检测到中断标志已设置
// 2. 清除中断标志(Thread.interrupted() 返回 true)
// 3. 抛出 InterruptedException
try {
Thread.sleep(10000); // 阻塞中
} catch (InterruptedException e) {
// 中断标志已被清除
// 需要根据业务决定:继续中断、恢复中断状态、退出
Thread.currentThread().interrupt(); // 正确做法:恢复中断标志
return;
}关键行为:sleep()/wait()/join() 在抛 InterruptedException 之前会清除中断标志,这意味着捕获异常后 Thread.interrupted() 返回 false。如果需要调用者知道中断发生,应在 catch 中重新设置中断标志。
响应 2:可中断 I/O(InterruptibleChannel)
对于实现了 InterruptibleChannel 的通道(如 SocketChannel、FileChannel):
// java.nio.channels.InterruptibleChannel
// interrupt() 时 blocker 机制的处理
// Thread 类内部:
public void interrupt() {
synchronized (blockerLock) {
Interruptible b = blocker;
if (b != null) {
interrupt0(); // 先设置中断标志
b.interrupt(this); // 调用 channel 的中断处理器
return;
}
}
interrupt0();
}具体行为:
interrupt0()设置 JVM 中断标志b.interrupt(this)调用Interruptible回调- 该回调关闭通道(
channel.close()) - 在通道上阻塞的线程收到
ClosedByInterruptException
// 示例:SocketChannel 的 interrupt 响应
SocketChannel channel = SocketChannel.open();
channel.connect(new InetSocketAddress("host", 8080));
channel.read(buffer); // 阻塞读
// 另一个线程调用 channel.socket().interrupt()
// → channel 被关闭
// → read() 抛出 ClosedByInterruptException响应 3:Selector 操作
java.nio.channels.Selector 的 select() 方法:
Selector selector = Selector.open();
// 另一个线程调用 selector.wakeup() 或在选择器上注册的通道的 interrupt()
// → select() 立即返回(返回值可能为 0)
// → 不会抛出异常interrupt() 时如果线程在 Selector.select() 中阻塞,JVM 调用 selector.wakeup() 使其立即返回。
5.4 interrupted() vs isInterrupted()
// java.lang.Thread
// 静态方法:清除中断标志并返回旧值
public static boolean interrupted() {
return currentThread().isInterrupted(true);
}
// 实例方法:不清除中断标志
public boolean isInterrupted() {
return isInterrupted(false);
}
// native 实现
private native boolean isInterrupted(boolean ClearInterrupted);| 方法 | 类型 | 是否清除标志 | 适用场景 |
|---|---|---|---|
Thread.interrupted() | 静态 | 是 | 检测中断并在检测后清除标志(如循环条件中) |
thread.isInterrupted() | 实例 | 否 | 仅查询中断状态,不修改 |
// 在循环中检测中断的正确模式
while (!Thread.interrupted()) {
// 执行任务...
// Thread.interrupted() 返回 true 后退出循环,同时清除标志
}
// 此处中断标志为 false
// 仅查询模式
if (Thread.currentThread().isInterrupted()) {
// 不修改中断标志
// 如果确认要处理中断,应调用 interrupted() 或重新设置标志
Thread.interrupted(); // 清除标志
// 或
Thread.currentThread().interrupt(); // 再次设置标志
}6. ThreadLocal 的 ThreadLocalMap
6.1 Thread 类中的 ThreadLocalMap 字段
// java.lang.Thread
public class Thread implements Runnable {
// 每个线程持有自己的 ThreadLocalMap
ThreadLocal.ThreadLocalMap threadLocals = null;
// 可继承的 ThreadLocal 值(留给 InheritableThreadLocal 使用)
ThreadLocal.ThreadLocalMap inheritableThreadLocals = null;
}每个 Thread 对象都有两个 ThreadLocalMap 引用:
threadLocals:普通的ThreadLocal值inheritableThreadLocals:可继承给子线程的值
6.2 ThreadLocalMap 的结构
// java.lang.ThreadLocal.ThreadLocalMap
static class ThreadLocalMap {
// Entry 继承 WeakReference,key 是 ThreadLocal 实例的弱引用
static class Entry extends WeakReference<ThreadLocal<?>> {
Object value; // 存储的实际值
Entry(ThreadLocal<?> k, Object v) {
super(k); // key 作为弱引用
value = v;
}
}
// 内部数组,初始容量 16
private Entry[] table;
// 当前存储的条目数
private int size = 0;
// 扩容阈值:table.length * 2/3
private int threshold;
}6.3 Entry 的弱引用设计
ThreadLocalMap.Entry 的引用关系:
Thread
│
│ threadLocals (强引用)
▼
┌──────────────────────┐
│ ThreadLocalMap │
│ ┌────────────────┐ │
│ │ Entry[] table │ │
│ └────────────────┘ │
│ │ │
│ ▼ │
│ ┌────────────────┐ │
│ │ Entry │ │
│ │ key ────弱引用──→ ThreadLocal 实例 (若 ThreadLocal 无外部强引用,可被 GC)
│ │ value (强引用) │ │
│ └────────────────┘ │
└──────────────────────┘为什么 key 要用弱引用?
如果 Entry 对 ThreadLocal 是强引用,那么即使业务代码中不再使用某个 ThreadLocal 对象,由于线程的 ThreadLocalMap 仍然持有该 ThreadLocal 的强引用,GC 将无法回收它,造成内存泄漏。
使用弱引用后:当 ThreadLocal 对象只有 Entry 的弱引用指向它时,GC 可以回收该 ThreadLocal,下一次操作 ThreadLocalMap 时清理这些 stale 的 Entry。
6.4 ThreadLocal.get() 的实现
// java.lang.ThreadLocal
public T get() {
// 1. 获取当前线程
Thread t = Thread.currentThread();
// 2. 获取当前线程的 ThreadLocalMap
ThreadLocalMap map = getMap(t);
if (map != null) {
// 3. 从 map 中查找当前 ThreadLocal 实例对应的 Entry
ThreadLocalMap.Entry e = map.getEntry(this);
if (e != null) {
@SuppressWarnings("unchecked")
T result = (T) e.value;
return result;
}
}
// 4. 未找到,初始化(首次 get 时调用)
return setInitialValue();
}
// 获取线程的 ThreadLocalMap
ThreadLocalMap getMap(Thread t) {
return t.threadLocals; // 直接读取 Thread 字段
}
// 初始化
private T setInitialValue() {
T value = initialValue(); // 子类可重写
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null) {
map.set(this, value);
} else {
createMap(t, value);
}
return value;
}6.5 ThreadLocal.set(value) 的实现
// java.lang.ThreadLocal
public void set(T value) {
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null) {
map.set(this, value); // key 为当前 ThreadLocal 实例
} else {
createMap(t, value);
}
}6.6 无锁访问
ThreadLocal 的线程安全分析:
ThreadLocalMap是每个线程私有的字段get()和set()都通过Thread.currentThread()获取当前线程的 map- 每个线程只修改自己的 map,不存在多线程竞争
- 因此
ThreadLocal.get()/set()无需加锁,是 O(1) 的高性能操作
// ThreadLocalMap.getEntry() - 无锁
private Entry getEntry(ThreadLocal<?> key) {
int i = key.threadLocalHashCode & (table.length - 1); // hash 索引
Entry e = table[i];
if (e != null && e.get() == key) {
return e; // 直接命中
} else {
return getEntryAfterMiss(key, i, e); // 未命中,线性探测
}
}7. ThreadLocalMap.set() 的 expungeStaleEntry()
7.1 set() 的完整流程
// java.lang.ThreadLocal.ThreadLocalMap
private void set(ThreadLocal<?> key, Object value) {
Entry[] tab = table;
int len = tab.length;
int i = key.threadLocalHashCode & (len - 1); // 计算 hash 槽位
// 线性探测:从 hash 槽位开始遍历
for (Entry e = tab[i]; e != null; e = tab[i = nextIndex(i, len)]) {
ThreadLocal<?> k = e.get();
if (k == key) {
// 情况 1:找到相同 key → 直接更新 value
e.value = value;
return;
}
if (k == null) {
// 情况 2:key 已被 GC 回收(过期 Entry)
// 用新 Entry 替换过期 Entry
replaceStaleEntry(key, value, i);
return;
}
// 情况 3:hash 冲突,继续线性探测
}
// 情况 4:找到空槽,插入新 Entry
tab[i] = new Entry(key, value);
int sz = ++size;
// 清理部分过期 Entry,若未清理且 size >= threshold 则扩容
if (!cleanSomeSlots(i, sz) && sz >= threshold)
rehash();
}四种情况总结:
| 情况 | 条件 | 操作 |
|---|---|---|
| 1 | 找到相同 key | 直接更新 value |
| 2 | 遇到过期 Entry(key == null) | replaceStaleEntry() 替换 |
| 3 | hash 冲突 | 继续线性探测 |
| 4 | 找到空槽 | 插入新 Entry + cleanSomeSlots() |
7.2 expungeStaleEntry(int staleSlot) — 过期 Entry 清理
这是 ThreadLocalMap 最核心的清理方法:
// java.lang.ThreadLocal.ThreadLocalMap
private int expungeStaleEntry(int staleSlot) {
Entry[] tab = table;
int len = tab.length;
// 1. 置空 staleSlot 处的过期 Entry
tab[staleSlot].value = null; // 帮助 value 被 GC
tab[staleSlot] = null;
size--;
// 2. 从下一个槽位开始,继续遍历清理
Entry e;
int i;
for (i = nextIndex(staleSlot, len); (e = tab[i]) != null; i = nextIndex(i, len)) {
ThreadLocal<?> k = e.get();
if (k == null) {
// 2.1 又是一个过期 Entry — 清理
e.value = null;
tab[i] = null;
size--;
} else {
// 2.2 非过期 Entry — 重新 hash 放置
int h = k.threadLocalHashCode & (len - 1);
if (h != i) {
// 当前槽位不是该 Entry 的 hash 目标位
tab[i] = null; // 清空当前槽
// 从 hash 目标位开始线性探测,找空位
while (tab[h] != null) {
h = nextIndex(h, len);
}
tab[h] = e;
}
// 如果 h == i,说明 Entry 就在其 hash 目标位,不需要移动
}
}
return i; // 返回第一个空槽的索引
}7.3 为什么要重新 hash?
考虑以下场景:
初始状态(线性探测,hash 冲突时顺延):
index: 0 1 2 3 4 5 6 7
[A] [B] [C] [D] [ ] [ ] [ ] [ ]
↑ ↑
A的hash 空槽(线性探测终止标记)
假设 Entry C 被清理(key == null):
index: 0 1 2 3 4 5 6 7
[A] [B] [X] [D] [ ] [ ] [ ] [ ]
↑
C被清理,槽位变空
此时 get(A) → hash(A)=0 → 找到 A ✓
get(B) → hash(B)=1 → 找到 B ✓
get(D) → hash(D)=3 → 但如果没有重新hash:
从 index=3 开始找 D,找到了 ✓ (恰好在原位)
但假设 D 原本的 hash 是 2(与 C 冲突才顺延到 3)
C 被清理后,D 应该在 index=2
重新 hash 后:
index: 0 1 2 3 4 5 6 7
[A] [B] [D] [ ] [ ] [ ] [ ] [ ]
↑
D被重新hash到正确的hash位置如果不重新 hash:当某个 Entry 被清理后,后面因 hash 冲突顺延的 Entry 无法被正确找到(线性探测在空槽处停止)。重新 hash 确保所有活跃 Entry 要么在 hash 目标位,要么在正确的探测链中。
7.4 cleanSomeSlots() — 启发式扫描
// java.lang.ThreadLocal.ThreadLocalMap
// 启发式地扫描清理过期 Entry
private boolean cleanSomeSlots(int i, int n) {
boolean removed = false;
Entry[] tab = table;
int len = tab.length;
do {
i = nextIndex(i, len);
Entry e = tab[i];
if (e != null && e.get() == null) {
n = len; // 发现过期 Entry → 重置扫描范围
removed = true;
i = expungeStaleEntry(i); // 完整清理一段
}
} while ((n >>>= 1) != 0); // 对数次扫描(n 不断右移)
return removed;
}对数扫描:n 初始为 len,每次右移一位,最多扫描 log2(len) 次。如果发现过期 Entry,重置 n = len 重新扫描。这就是"启发式"的含义——在不遍历全表的前提下尽可能清理。
7.5 内存泄漏场景
内存泄漏发生的条件:
1. ThreadLocal 对象被 GC 回收(强引用不存在)
2. Thread 线程仍然存活(线程池中的线程长期不销毁)
3. Entry.value 是强引用,不会被 GC
泄漏链:
Thread → ThreadLocalMap → Entry (key=null, value=强引用) → value 对象不可回收典型场景:
// 内存泄漏示例
public class LeakExample {
private static final ThreadLocal<byte[]> BIG_DATA = ThreadLocal.withInitial(
() -> new byte[1024 * 1024 * 100] // 100MB
);
public void process() {
BIG_DATA.get(); // 线程持有 100MB 数据
// 忘记调用 BIG_DATA.remove()
// 线程池中的线程不会销毁,100MB 一直占用
}
}预防措施:
// 正确使用模式
try {
threadLocal.set(value);
// ... 业务逻辑 ...
} finally {
threadLocal.remove(); // 显式清理
// remove() 内部调用 ThreadLocalMap.remove(this)
// 清除 Entry,避免内存泄漏
}8. InheritableThreadLocal 的父子继承
8.1 父线程传递值到子线程
普通的 ThreadLocal 在子线程中无法访问父线程设置的值。InheritableThreadLocal 允许在创建子线程时自动继承父线程的值:
// java.lang.InheritableThreadLocal
public class InheritableThreadLocal<T> extends ThreadLocal<T> {
// 子类可重写此方法以转换值
protected T childValue(T parentValue) {
return parentValue; // 默认直接继承
}
// 返回继承用的 ThreadLocalMap
ThreadLocalMap getMap(Thread t) {
return t.inheritableThreadLocals;
}
// 创建继承用的 ThreadLocalMap
void createMap(Thread t, T firstValue) {
t.inheritableThreadLocals = new ThreadLocalMap(this, firstValue);
}
}8.2 Thread.init() 中的继承逻辑
// java.lang.Thread.init()(简化)
private void init(ThreadGroup g, Runnable target, String name,
long stackSize, AccessControlContext acc,
boolean inheritThreadLocals) {
// ...
Thread parent = currentThread();
// 如果父线程有 inheritableThreadLocals,拷贝到子线程
if (inheritThreadLocals && parent.inheritableThreadLocals != null) {
this.inheritableThreadLocals =
ThreadLocal.createInheritedMap(parent.inheritableThreadLocals);
}
// ...
}
// java.lang.ThreadLocal
static ThreadLocalMap createInheritedMap(ThreadLocalMap parentMap) {
return new ThreadLocalMap(parentMap);
}
// ThreadLocalMap 的复制构造器
private ThreadLocalMap(ThreadLocalMap parentMap) {
Entry[] parentTable = parentMap.table;
int len = parentTable.length;
setThreshold(len);
table = new Entry[len];
for (int j = 0; j < len; j++) {
Entry e = parentTable[j];
if (e != null) {
@SuppressWarnings("unchecked")
ThreadLocal<Object> key = (ThreadLocal<Object>) e.get();
if (key != null) {
// 调用 childValue() 转换值
Object value = key.childValue(e.value);
Entry c = new Entry(key, value);
int h = key.threadLocalHashCode & (len - 1);
while (table[h] != null)
h = nextIndex(h, len);
table[h] = c;
size++;
}
}
}
}8.3 继承链示意图
父线程(parent thread)
│
│ inheritableThreadLocals = { key1 → value1, key2 → value2 }
│
├── new Thread(() -> { ... }) // 创建子线程
│ │
│ ▼
│ Thread.init():
│ child.inheritableThreadLocals =
│ copyOf(parent.inheritableThreadLocals)
│ // 对每个 key 调用 key.childValue(parentValue)
│
▼
子线程(child thread)— 独立拷贝,互不影响
父线程:inheritableThreadLocals = { key1 → v1_updated, key2 → v2 }
子线程:inheritableThreadLocals = { key1 → v1_original, key2 → v2 }注意:继承发生在 new Thread() 时(调用 init()),子线程启动后父线程对 InheritableThreadLocal 的修改不会影响子线程。
8.4 应用场景
// 应用场景 1:传递请求 ID
public class RequestContext {
private static final InheritableThreadLocal<String> REQUEST_ID =
new InheritableThreadLocal<>();
public static void setRequestId(String id) {
REQUEST_ID.set(id);
}
public static String getRequestId() {
return REQUEST_ID.get();
}
}
// 在 HTTP 请求处理中
RequestContext.setRequestId(UUID.randomUUID().toString());
// 异步任务自动继承请求 ID
CompletableFuture.runAsync(() -> {
// 这里可以获取到父线程的请求 ID
String requestId = RequestContext.getRequestId();
// ...
});
// 应用场景 2:事务上下文传递
// 分布式事务中,父线程的事务上下文自动传递给子线程8.5 线程池的注意点
InheritableThreadLocal 只在 new Thread() 时继承一次。对于线程池(ThreadPoolExecutor),线程被复用,InheritableThreadLocal 无法传递每次提交的任务上下文:
ExecutorService executor = Executors.newFixedThreadPool(2);
// 第一次提交
RequestContext.setRequestId("req-1");
executor.submit(() -> {
// 线程 A 执行:能拿到 "req-1"
System.out.println(RequestContext.getRequestId());
});
RequestContext.setRequestId("req-2");
executor.submit(() -> {
// 如果是同一线程 A 执行:还是 "req-1"!不是 "req-2"
System.out.println(RequestContext.getRequestId());
});解决方案:使用 TransmittableThreadLocal(阿里开源)或每次提交任务时手动传递上下文。
9. 额外关键方法
9.1 ThreadGroup 的树形结构
ThreadGroup 以树形结构组织线程和子线程组:
// java.lang.ThreadGroup(部分源码)
public class ThreadGroup implements Thread.UncaughtExceptionHandler {
private final ThreadGroup parent; // 父线程组
private final String name;
private int maxPriority; // 组内线程最大优先级
private boolean destroyed; // 是否已销毁
private boolean daemon; // 守护线程组
// 子线程组(数组,非树形动态结构)
ThreadGroup groups[]; // 子线程组列表
int ngroups; // 子线程组数量
// 组内线程
int nthreads; // 线程数量
Thread threads[]; // 线程列表
}树形结构:
system (根 ThreadGroup)
├── main
│ ├── Thread-1
│ ├── Thread-2
│ └── worker-group (子 ThreadGroup)
│ ├── worker-1
│ └── worker-2
└── InnocuousThreadGroup
└── ...ThreadGroup 主要用于:
- 批量操作:
activeCount()、enumerate() - 统一异常处理:
uncaughtException() - 安全沙箱:控制线程访问权限
9.2 Thread.yield() 的语义
// java.lang.Thread
public static native void yield();yield() 是一个 native 方法,语义是"提示调度器当前线程愿意让出 CPU":
// Linux 实现
// hotspot/os/linux/os_linux.cpp
void os::yield() {
sched_yield(); // POSIX 系统调用,将当前线程移到运行队列尾部
}
// Windows 实现
// hotspot/os/windows/os_windows.cpp
void os::yield() {
// SwitchToThread 让出当前时间片
// 如果没有其他线程可运行,立即返回
SwitchToThread();
}语义要点:
| 方面 | 说明 |
|---|---|
| 纯提示 | JVM 不保证 yield() 有任何效果 |
| 不释放锁 | yield() 不会释放 synchronized 锁 |
| 不再推荐 | 大多数场景下 yield() 不可靠,推荐用 LockSupport.park() 或等待通知机制 |
| 短等待 | 适用于自旋等待的优化,如 while (!ready) Thread.yield() |
9.3 Thread.onSpinWait()(JDK 9+)
// java.lang.Thread(JDK 9+)
@HotSpotIntrinsicCandidate
public static void onSpinWait() {
// 由 JVM 实现,通过 intrinsic 替换
}onSpinWait() 用于自旋等待循环中,向 CPU 提示当前线程处于"忙等待"状态:
// JDK 9+ 推荐的 spin-wait 模式
volatile boolean eventReady = false;
// 等待事件(不阻塞线程)
while (!eventReady) {
Thread.onSpinWait(); // CPU 提示
}原理:
- x86:映射为
PAUSE指令(Intel)或YIELD指令(ARM) PAUSE指令提示 CPU 当前是 spin-loop,减少功耗 + 提高超线程性能- 不同于
yield():onSpinWait()不会触发线程调度,仅降低 CPU 功耗
9.4 StackWalker(JDK 9+)调用栈遍历
// java.lang.StackWalker(JDK 9+)
// 轻量级调用栈遍历 API,替代 Throwable.getStackTrace()
public final class StackWalker {
// 获取 StackWalker 实例
public static StackWalker getInstance();
public static StackWalker getInstance(Option option);
// 遍历调用栈
public void forEach(Consumer<? super StackFrame> action);
// 获取调用者类
public Class<?> getCallerClass();
}典型用法:
// JDK 9+ 获取调用者类(替代之前的 sun.reflect.Reflection.getCallerClass)
Class<?> caller = StackWalker.getInstance(Option.RETAIN_CLASS_REFERENCE)
.getCallerClass();
// 遍历调用栈
StackWalker.getInstance().forEach(frame -> {
System.out.println(frame.getClassName() + "." + frame.getMethodName());
});与 Thread.getStackTrace() 的关系:
| 方法 | 特点 |
|---|---|
Thread.getStackTrace() | 返回整个栈的快照,开销较大 |
StackWalker.walk() | 惰性遍历,更高效,支持过滤和映射 |
new Throwable().getStackTrace() | 创建异常对象,开销最大 |
10. 关键类结构一览
java.lang.Runnable
└── void run() ← 函数式接口,定义任务
java.lang.Thread implements Runnable
├── threadStatus (volatile) ← 线程状态,volatile 保证可见性
├── name, priority, daemon ← 线程元信息
├── threadLocals ← ThreadLocal.ThreadLocalMap(普通 ThreadLocal)
├── inheritableThreadLocals ← ThreadLocal.ThreadLocalMap(可继承 ThreadLocal)
├── blocker/blockerLock ← InterruptibleChannel 中断支持
├── group (ThreadGroup) ← 线程所属线程组
│
├── start() → start0() native ← 启动线程(只可调用一次)
├── run() ← 如果传入 Runnable 则调用其 run()
├── sleep(long) → sleep0() native ← 线程休眠
├── join(long) → wait(long) ← 等待线程终止
├── interrupt() → interrupt0() ← 设置中断标志
├── interrupted() / isInterrupted()← 查询 / 清除中断标志
├── yield() native ← 提示让出 CPU
├── onSpinWait() ← JDK 9+ spin-loop hint
└── getState() ← 返回 Thread.State 枚举
java.lang.Thread.State (enum)
├── NEW ← 未 start()
├── RUNNABLE ← 可运行或正在运行
├── BLOCKED ← 等待监视器锁
├── WAITING ← 无限期等待
├── TIMED_WAITING ← 超时等待
└── TERMINATED ← 已终止
java.lang.ThreadGroup
├── parent ← 父线程组
├── groups[] ← 子线程组数组
├── threads[] ← 组内线程数组
└── uncaughtException()← 未捕获异常处理
java.lang.ThreadLocal<T>
├── ThreadLocalMap (内部类)
│ ├── Entry[] table ← 开放地址法存储
│ ├── Entry extends WeakReference<ThreadLocal<?>>
│ │ └── value: T
│ ├── set() → replaceStaleEntry / cleanSomeSlots / expungeStaleEntry
│ ├── get() → getEntry / getEntryAfterMiss
│ └── remove()
├── get() → Thread.currentThread().threadLocals.get(this)
├── set(value) → Thread.currentThread().threadLocals.set(this, value)
└── remove() → Thread.currentThread().threadLocals.remove(this)
java.lang.InheritableThreadLocal<T> extends ThreadLocal<T>
├── childValue(parentValue) ← 子类可重写,转换继承的值
├── getMap() → t.inheritableThreadLocals
└── createMap() → t.inheritableThreadLocals = new ThreadLocalMap(...)总结
Thread.start()与Runnable的模板方法模式:start0()native 方法通过JVM_StartThread→pthread_create/CreateThread创建 OS 线程,然后回调Thread.run(),run()再调用Runnable.run()。threadStatus与 Thread.State:Thread.State枚举将 JVM 内部线程状态映射为 6 个 Java 可读状态,getState()通过 JVM native 查询当前真实状态。sleep()/join()/wait()的阻塞原语:底层依赖操作系统原语(Linuxpthread_cond_timedwait、WindowsWaitForSingleObject),中断时抛出InterruptedException并清除中断标志。interrupt()的三种响应模型:基于 JVM 中断标志,分别对应可中断阻塞(抛异常)、InterruptibleChannel(关闭通道)、Selector(wakeup 返回)。ThreadLocal的线程隔离机制:每个线程持有自己的ThreadLocalMap,Entry使用弱引用 key 防止ThreadLocal泄漏,expungeStaleEntry()通过开放地址法 + 重新 hash 清理过期 Entry。InheritableThreadLocal的父子继承:在Thread.init()时拷贝父线程的inheritableThreadLocals,通过childValue()支持值转换,但每次new Thread()仅继承一次,线程池场景需特别注意。ThreadGroup的树形管理:线程组以树形结构组织线程,支持批量操作和统一异常处理,但在现代 Java 并发框架中已逐渐被ExecutorService替代。