一文搞懂LockSupport原理
本文会先梳理 LockSupport 的核心原理,再逐一对比它与 synchronized、Thread.sleep()、Object.wait()、Condition.await() 的区别,同时解答 notify()/unpark() 提前调用的关键问题,帮你彻底理清 Java 并发中线程阻塞/唤醒的核心逻辑。
一、LockSupport 核心原理回顾
LockSupport 是 JDK 并发包的底层工具类,核心是许可(Permit)机制:
- 每个线程绑定一个「许可」,许可只有两种状态:可用(1)、不可用(0),且最多只能有 1 个许可(不可累加);
park():尝试获取许可,获取到则消耗许可(变为 0)并直接返回;获取不到则阻塞线程,直到被唤醒/中断/超时;unpark(Thread t):为线程 t 发放许可(若已存在则无效果),若线程正阻塞在 park() 上则唤醒它;- 底层依赖
sun.misc.Unsafe调用 JVM 的 Parker 类,最终通过操作系统的 futex 系统调用实现线程挂起/唤醒。
二、各类同步方法的核心区别对比
1. LockSupport vs synchronized
| 维度 | LockSupport | synchronized |
|---|---|---|
| 核心作用 | 线程阻塞/唤醒的底层工具 | 实现方法/代码块的互斥与同步 |
| 底层实现 | Unsafe + Parker + 操作系统 futex | 偏向锁→轻量级锁→重量级锁(监视器锁) |
| 使用方式 | 静态方法直接调用(park/unpark) | 修饰方法/代码块(隐式获取/释放锁) |
| 阻塞/唤醒粒度 | 精准唤醒指定线程(unpark(Thread t)) | 随机唤醒一个(notify())/全部(notifyAll()) |
| 前置条件 | 无需持有任何锁 | 必须先获取对象的监视器锁 |
| 中断响应 | 响应中断,返回后需手动检查中断状态 | 响应中断,抛出 InterruptedException |
| 灵活性 | 极高(支持超时、先unpark后park安全) | 较低(锁释放仅在代码块结束/异常时) |
核心差异:synchronized 是「锁」,侧重资源互斥;LockSupport 是「线程控制工具」,是实现锁的基础。比如 ReentrantLock 内部就是通过 AQS 调用 LockSupport 实现线程阻塞。
2. Thread.sleep() vs Object.wait()
| 维度 | Thread.sleep(long ms) | Object.wait() |
|---|---|---|
| 核心作用 | 让线程休眠指定时间,不释放资源 | 让线程等待,释放持有的监视器锁 |
| 前置条件 | 无(可在任意位置调用) | 必须在 synchronized 块中(持有对象锁) |
| 锁释放 | 不释放任何锁(包括 synchronized 锁) | 释放当前对象的监视器锁 |
| 唤醒方式 | 超时自动唤醒 / 线程中断 | notify()/notifyAll() 唤醒 / 中断 / 超时 |
| 中断处理 | 抛出 InterruptedException | 抛出 InterruptedException |
| 状态变化 | 线程进入 TIMED_WAITING | 线程进入 WAITING/TIMED_WAITING |
示例对比:
// sleep 不释放锁
synchronized (obj) {
Thread.sleep(1000); // 持有锁休眠,其他线程无法获取 obj 锁
}
// wait 释放锁
synchronized (obj) {
obj.wait(1000); // 释放 obj 锁,其他线程可获取;超时后重新竞争锁
}
3. Object.wait() vs Condition.await()
Condition 是 JUC 包中配合 Lock 使用的同步工具(如 ReentrantLock.newCondition()),本质是 LockSupport 的上层封装:
| 维度 | Object.wait() | Condition.await() |
|---|---|---|
| 依赖锁类型 | synchronized 监视器锁 | Lock 接口实现(如 ReentrantLock) |
| 等待队列数量 | 每个对象只有 1 个等待队列 | 每个 Condition 对应 1 个等待队列(可多个) |
| 唤醒粒度 | 只能随机唤醒一个/全部 | 可精准唤醒指定 Condition 队列的线程 |
| 方法丰富度 | 仅 wait()/wait(long) | 支持 await()/awaitNanos()/awaitUntil()/awaitUninterruptibly() 等 |
| 中断响应 | 抛出 InterruptedException | 可选择响应中断(await())或不响应(awaitUninterruptibly()) |
核心差异:Condition 解决了 synchronized 中 wait/notify 唤醒粒度不足的问题,比如生产者消费者模型中,可分别创建「生产者 Condition」和「消费者 Condition」,精准唤醒对应线程。
4. Thread.sleep() vs LockSupport.park()
| 维度 | Thread.sleep(long ms) | LockSupport.park() |
|---|---|---|
| 核心机制 | 纯时间驱动的休眠 | 许可机制驱动的阻塞 |
| 锁释放 | 不释放任何锁 | 不释放任何锁 |
| 唤醒方式 | 超时自动唤醒 / 中断 | unpark() 唤醒 / 中断 / 超时(parkNanos) |
| 前置条件 | 无 | 无 |
| 先唤醒后阻塞 | 不支持(sleep(1000) 必须等 1000ms) | 支持(先 unpark 后 park 直接返回) |
| 中断处理 | 抛出 InterruptedException | 响应中断但不抛异常(需手动检查) |
| 诊断性 | 无阻塞原因记录 | 支持 park(blocker) 记录阻塞原因 |
关键区别:sleep 是「被动等待时间结束」,park 是「主动等待许可」;park 更灵活,是并发工具的底层选择,sleep 仅用于简单的时间休眠。
5. Object.wait() vs LockSupport.park()
| 维度 | Object.wait() | LockSupport.park() |
|---|---|---|
| 前置条件 | 必须持有对象监视器锁(synchronized) | 无(可在任意位置调用) |
| 锁释放 | 释放监视器锁 | 不释放任何锁 |
| 唤醒方式 | notify()/notifyAll()(随机/全部) | unpark(Thread t)(精准指定线程) |
| 先唤醒后阻塞 | 不安全(先 notify 后 wait 会丢失唤醒) | 安全(先 unpark 后 park 直接返回) |
| 许可机制 | 无 | 基于许可(Permit)机制 |
| 中断处理 | 抛出 InterruptedException | 响应中断但不抛异常 |
核心差异:wait() 是「同步锁层面的等待」(释放锁),park() 是「线程层面的阻塞」(不释放锁);park 无需依赖锁,灵活性和安全性更高。
三、关键问题解答
1. 如果在 wait() 之前执行了 notify() 会怎样?
结果:线程调用 wait() 后会永久阻塞(除非被中断)。
原因:
- wait()/notify() 依赖对象的「等待队列」:notify() 会唤醒队列中一个线程,但如果调用 notify() 时队列中无等待线程,这个唤醒信号会丢失;
- 后续线程调用 wait() 时,会进入等待队列,但已没有 notify() 信号唤醒它,最终线程会一直阻塞。
示例验证:
public class WaitNotifyTest {
public static void main(String[] args) throws InterruptedException {
Object obj = new Object();
// 先调用 notify()
synchronized (obj) {
obj.notify(); // 此时无等待线程,信号丢失
}
// 后调用 wait() → 永久阻塞
new Thread(() -> {
synchronized (obj) {
try {
obj.wait(); // 无 notify() 唤醒,一直阻塞
System.out.println("线程被唤醒"); // 永远不会执行
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}).start();
}
}
2. 如果在 park() 之前执行了 unpark() 会怎样?
结果:线程调用 park() 后直接返回,不会阻塞。
原因:
- LockSupport 基于「许可机制」,unpark() 会为线程发放许可(最多 1 个);
- 即使线程还未调用 park(),许可也会被保存,后续 park() 会直接消耗许可并返回,不会阻塞。
示例验证:
public class ParkUnparkTest {
public static void main(String[] args) throws InterruptedException {
Thread t = new Thread(() -> {
try {
// 先等待 1 秒,让主线程先执行 unpark()
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("线程准备调用 park()");
LockSupport.park(); // 许可已存在,直接返回
System.out.println("线程 park() 后继续执行");
});
t.start();
// 先调用 unpark() 发放许可
LockSupport.unpark(t);
System.out.println("主线程已执行 unpark()");
}
}
输出结果:
主线程已执行 unpark()
线程准备调用 park()
线程 park() 后继续执行
四、总结
- LockSupport 核心:基于「许可机制」实现线程阻塞/唤醒,无锁依赖、支持精准唤醒、先 unpark 后 park 安全,是 JUC 锁的底层基础;
- 核心区别关键点:
- sleep 不释放锁、时间驱动;wait 释放监视器锁、依赖 synchronized;park 不释放锁、许可驱动;
- Condition 是 wait/notify 的增强版,支持多队列精准唤醒;
- 关键问题结论:
- 先 notify 后 wait → 信号丢失,线程永久阻塞;
- 先 unpark 后 park → 许可保留,线程直接返回,无阻塞。
百流积聚,江河是也;文若化风,可以砾石。

浙公网安备 33010602011771号