LockSupport / Unsafe 底层并发原语源码精读
概述
LockSupport 是 JUC 的"停车"原语,AQS 的 park / unpark 全部委托给它;sun.misc.Unsafe 则是 JVM 的"后门",提供 CAS、内存读写、类实例化等超越 Java 语言规范的能力。理解这两层,才能看清 ReentrantLock、LongAdder、CompletableFuture 的底层机制。本文基于 OpenJDK 21 源码拆解。
一、LockSupport 的 park / unpark
1.1 核心实现
// java.util.concurrent.locks.LockSupport
public static void park() {
U.park(false, 0L); // Unsafe.park:永久阻塞(直到 unpark 或中断)
}
public static void parkNanos(long nanos) {
if (nanos > 0)
U.park(false, nanos); // 带超时的阻塞
}
public static void parkUntil(long deadline) {
U.park(true, deadline); // 绝对时间截止
}
public static void unpark(Thread thread) {
if (thread != null)
U.unpark(thread);
}U 是 Unsafe 实例,park / unpark 是 native 方法:
// jdk.internal.misc.Unsafe
@IntrinsicCandidate
public native void park(boolean isAbsolute, long time);
@IntrinsicCandidate
public native void unpark(Object thread);native 实现(HotSpot):
park(false, 0L):
Linux → pthread_cond_wait(条件变量等待)
Windows→ WaitForSingleObject(事件对象)
语义 :无许可则阻塞;有许可则消耗许可立即返回
unpark(thread):
先设置该线程的"许可"(permit,原子置 1)
Linux → pthread_cond_signal
Windows→ SetEvent
语义 :即使目标线程尚未 park,许可也会保留——下次 park 立即返回许可模型是
LockSupport的精髓:unpark与park不需要严格配对,unpark可以先于park执行,许可只会保留一份(重复unpark不会累积)。
1.2 与 Object.wait 的对比
| 维度 | LockSupport.park | Object.wait |
|---|---|---|
| 前置条件 | 无(不需要持锁) | 必须先持有对象监视器 |
| 唤醒方式 | unpark(thread) 精确唤醒指定线程 | notify / notifyAll |
| 中断响应 | park 立即返回(不抛异常) | 抛 InterruptedException |
| 超时 | parkNanos / parkUntil | wait(timeout) |
| 许可模型 | 有(可提前 unpark) | 无 |
二、Blocker 线程诊断
// LockSupport 的内部工具
private static void setBlocker(Thread t, Object arg) {
U.putReference(t, PARKBLOCKER, arg); // 记录阻塞对象到线程字段
}
public static void park(Object blocker) {
Thread t = Thread.currentThread();
setBlocker(t, blocker); // ① 记录"因为什么阻塞"
U.park(false, 0L); // ② 真正阻塞
setBlocker(t, null); // ③ 被唤醒后清空
}
public static Object getBlocker(Thread t) {
if (t == null) throw new NullPointerException();
return U.getReference(t, PARKBLOCKER); // 读取阻塞对象
}PARKBLOCKER 是 Thread.parkBlocker 字段的偏移量,由 VarHandle 获取:
private static final VarHandle PARKBLOCKER;
static {
try {
MethodHandles.Lookup l = MethodHandles.lookup();
PARKBLOCKER = l.findVarHandle(Thread.class, "parkBlocker", Object.class);
} catch (ReflectiveOperationException e) {
throw new ExceptionInInitializerError(e);
}
}诊断价值:jstack 输出中每个线程的 parking to wait for <0x...> (java.util.concurrent.SynchronousQueue$TransferStack) 正是 Blocker 的可读化展示——线上排查死锁时,Blocker 能直接告诉你线程在等什么。
三、Unsafe 的内存操作
3.1 offset 偏移量读取
Unsafe 的一切字段操作都基于字段偏移量(字段在对象内存布局中的位置):
// sun.misc.Unsafe
public native long objectFieldOffset(Field f); // 实例字段偏移量
public native long staticFieldOffset(Field f); // 静态字段偏移量
public native Object staticFieldBase(Field f); // 静态字段所在类对象读取模式:
Unsafe.getInt(Object obj, long offset):
obj == null → 静态字段:offset = staticFieldOffset(field),
base = staticFieldBase(field),读 (base, offset)
obj != null → 实例字段:offset = objectFieldOffset(field),读 (obj, offset)// 典型用法:AtomicInteger 初始化偏移量
long valueOffset = Unsafe.objectFieldOffset(
AtomicInteger.class.getDeclaredField("value"));3.2 CAS 原子操作
public final native boolean compareAndSwapInt(Object o, long offset,
int expected, int x);底层由 CPU 指令保证:
x86 → lock cmpxchg(带 LOCK 前缀的 compare-and-exchange)
ARM → LDXR/STXR(load-exclusive / store-exclusive 循环)
流程:比较内存值与 expected → 相等则写入 x 并返回 true
不等则不动并返回 falsecompareAndSwapObject / compareAndSwapLong 同理,是 AQS、原子类、并发容器的基石。
3.3 volatile 读与懒写
public native Object getObjectVolatile(Object o, long offset);
public native void putObjectVolatile(Object o, long offset, Object x);
public native void putOrderedObject(Object o, long offset, Object x);
public native void putOrderedInt(Object o, long offset, int x);getObjectVolatile:volatile 读 —— acquire 语义
禁止其后的读写重排序到读取之前
保证读取到最新写入值(配合 cache coherence)
putObjectVolatile:volatile 写 —— release 语义
禁止其前的读写重排序到写入之后
putOrderedObject(lazySet):release 写但不等 store buffer 冲刷
不保证立即可见(其他线程可能稍后读到旧值)
无内存屏障成本 → 高吞吐(AtomicInteger.lazySet 用它)
putOrdered*只保证"不会后移"到后续普通写之后(store-store 屏障),不保证"立即可见"——适用于写者多、读者能容忍延迟的计数场景。
四、allocateInstance 绕过构造器
public native Object allocateInstance(Class<?> cls) throws InstantiationException;作用:只分配内存、不调用任何构造器(连默认构造器都不执行),字段保持 JVM 默认值(0 / null / false)。
// 用法示例(反序列化场景)
Object obj = unsafe.allocateInstance(MyClass.class);
// obj 已存在,但构造器未执行,字段全为默认值典型用途:
- 序列化框架(Kryo、Fastjson)反序列化时绕过构造器
- 创建不执行构造器逻辑的对象(如测试工具)
- 延迟初始化(字段稍后手动赋值)
风险:绕过构造器意味着跳过构造器中的校验与初始化逻辑,滥用可能产生状态不完整的对象。
五、VarHandle vs Unsafe
VarHandle 是 JDK 9 引入的"安全版 Unsafe",AQS、原子类底层已大量迁移:
// VarHandle 方式(类型安全)
MethodHandles.Lookup l = MethodHandles.lookup();
VarHandle V = l.findVarHandle(AtomicInteger.class, "value", int.class);
V.compareAndSet(this, expect, update);
// Unsafe 方式(易错)
unsafe.compareAndSwapInt(this, valueOffset, expect, update);| 维度 | VarHandle | Unsafe |
|---|---|---|
| 类型安全 | 编译期校验字段类型与操作类型 | 全靠 offset 长整型,类型错误运行时才暴露 |
| 支持形式 | 方法句柄(可做一等公民传递) | 方法调用 |
| 访问模式 | compareAndSet / getAcquire / setOpaque 等全模式 | 有限制 |
| 可达性分析 | GC 友好(引用关系对分析器可见) | 对象引用经 offset 读写,逃逸分析困难 |
| 平台可移植 | 规范定义,跨 JVM 实现 | 依赖 HotSpot 内部语义 |
Unsafe 仍不可替代的场景:allocateInstance、getLong(byte[], offset) 等非对称访问、addressSize 等平台内省——这些属于 JDK 内部或框架才该用的"危险 API"。
六、实现要点
LockSupport / Unsafe 核心:
park/unpark:Unsafe native → pthread_cond_wait / WaitForSingleObject
许可模型:unpark 可提前,许可只保留一份
Blocker:park 记录阻塞对象 → jstack 可读诊断
offset:objectFieldOffset / staticFieldOffset 定位字段
CAS:x86 lock cmpxchg / ARM LDXR-STXR
volatile 读:getObjectVolatile(acquire)
懒写:putOrderedObject(store-store,高吞吐低可见性)
allocateInstance:绕过构造器(反序列化用)
VarHandle:类型安全的 Unsafe 替代(JDK 9+)
常见陷阱:
putOrdered* 不保证即时可见(读可能滞后)
allocateInstance 跳过构造器初始化
park 会被中断唤醒(需循环检查条件)
Unsafe 是危险 API,业务代码应使用 VarHandle 或原子类