Java 线程等待唤醒机制深度解析:synchronized、ReentrantLock、LockSupport 底层实现对比
日期 :2026-05-15
标签 :Java并发、等待唤醒、ObjectMonitor、AQS、LockSupport、Condition、HotSpot源码
阅读建议:配合 OpenJDK 源码理解三种机制的底层联系与差异
目录
- 三种机制总览对比
- [synchronized 的等待唤醒](#synchronized 的等待唤醒)
- [ReentrantLock 的 Condition 机制](#ReentrantLock 的 Condition 机制)
- [LockSupport 的许可机制](#LockSupport 的许可机制)
- [底层联系:都依赖 LockSupport](#底层联系:都依赖 LockSupport)
- 经典面试题解析
一、三种机制总览对比
| 特性 | synchronized |
ReentrantLock |
LockSupport |
|---|---|---|---|
| 等待队列 | 内置 WaitSet(对象头关联) |
Condition 队列(AQS 内部) |
无队列,纯线程级 |
| 唤醒方式 | notify() / notifyAll() |
signal() / signalAll() |
unpark() |
| 等待位置 | 对象 Monitor 的 WaitSet |
AQS Condition 队列 |
线程自身状态 |
| 锁释放 | wait() 自动释放锁 |
await() 自动释放锁 |
不持有锁,不涉及 |
| 中断响应 | 抛 InterruptedException |
抛 InterruptedException |
不抛异常,直接返回 |
| 底层实现 | ObjectMonitor (C++) | AQS + LockSupport | Unsafe.park/unpark |
| 灵活性 | 单一等待队列 | 多个 Condition 队列 | 最底层,无队列概念 |
二、synchronized 的等待唤醒
2.1 核心结构:ObjectMonitor
┌─────────────────────────────────────────┐
│ ObjectMonitor (C++ 对象) │
│ ┌─────────────────────────────────────┐│
│ │ _owner = Thread-A(持有锁的线程) ││
│ └─────────────────────────────────────┘│
│ │
│ ┌──────────────┐ ┌──────────────┐ │
│ │ _WaitSet │ │ _EntryList │ │
│ │ (等待池) │ │ (阻塞池) │ │
│ │ │ │ │ │
│ │ Thread-B ──→│ │ Thread-C ──→│ │
│ │ (调用wait) │ │ (竞争锁失败) │ │
│ │ Thread-D ──→│ │ Thread-E ──→│ │
│ │ WAITING │ │ BLOCKED │ │
│ └──────────────┘ └──────────────┘ │
│ │
│ wait() → 进 _WaitSet(释放锁) │
│ notify() → 从 _WaitSet 移到 _EntryList │
│ 锁释放 → 从 _EntryList 唤醒竞争锁 │
└─────────────────────────────────────────┘
2.2 代码示例
java
public class SynchronizedWaitNotify {
private final Object lock = new Object();
private boolean flag = false;
// 生产者
class Producer extends Thread {
@Override
public void run() {
synchronized (lock) {
while (flag) { // 【关键】防止虚假唤醒
try {
lock.wait(); // 1. 释放锁 2. 进入 _WaitSet 等待
} catch (InterruptedException e) {
e.printStackTrace();
}
}
// 生产数据
System.out.println("生产数据");
flag = true;
lock.notifyAll(); // 唤醒 _WaitSet 中所有线程
}
}
}
// 消费者
class Consumer extends Thread {
@Override
public void run() {
synchronized (lock) {
while (!flag) { // 【关键】防止虚假唤醒
try {
lock.wait(); // 释放锁,等待
} catch (InterruptedException e) {
e.printStackTrace();
}
}
// 消费数据
System.out.println("消费数据");
flag = false;
lock.notifyAll(); // 唤醒生产者
}
}
}
}
2.3 底层 C++ 实现
ObjectMonitor::wait()
cpp
void ObjectMonitor::wait(jlong millis, bool interruptable, TRAPS) {
JavaThread* self = THREAD;
// 1. 检查中断
if (interruptable && Thread::is_interrupted(self, true)) {
throw new InterruptedException();
}
// 2. 构造等待节点,加入 _WaitSet
ObjectWaiter node(self);
node.set_state(ObjectWaiter::TS_WAIT);
AddWaiter(&node); // 加入 _WaitSet 队列
// 3. 【关键】释放锁!
exit(true, self); // monitor exit,_owner = NULL
// 4. 线程状态改为 WAITING,park 阻塞
self->_ParkEvent->park(millis); // 底层 LockSupport.park
// 5. 被唤醒后(notify 或 interrupt),重新竞争锁
EnterI(self); // 进入 _EntryList 排队,获取锁后返回
// 6. 获取锁后,检查中断
if (interruptable && Thread::is_interrupted(self, true)) {
throw new InterruptedException();
}
}
ObjectMonitor::notify()
cpp
void ObjectMonitor::notify(TRAPS) {
// 1. 检查锁持有者
if (_owner != THREAD) {
throw new IllegalMonitorStateException();
}
if (_WaitSet == NULL) return; // 没有等待线程
// 2. 从 _WaitSet 头部取一个线程
ObjectWaiter* iterator = _WaitSet;
_WaitSet = iterator->_next; // 链表移除
// 3. 【关键】移到 _EntryList,不是直接给锁!
iterator->set_state(ObjectWaiter::TS_ENTER);
iterator->_next = _EntryList;
_EntryList = iterator;
// 4. 唤醒线程(unpark),但唤醒后还要竞争锁
// 这就是为什么 notify 后不会立刻执行,要等当前线程释放锁
}
三、ReentrantLock 的 Condition 机制
3.1 核心结构:AQS + Condition
┌─────────────────────────────────────────┐
│ ReentrantLock (AQS) │
│ │
│ ┌─────────────────────────────────────┐│
│ │ state = 1(锁被持有) ││
│ │ ownerThread = Thread-A ││
│ └─────────────────────────────────────┘│
│ │
│ ┌─────────────────────────────────────┐│
│ │ CLH 队列(竞争锁的阻塞线程) ││
│ │ Node-C ← Node-D ← Node-E ││
│ │ BLOCKED ││
│ └─────────────────────────────────────┘│
│ │
│ lock.newCondition() → ConditionObject │
│ ┌─────────────────────────────────────┐│
│ │ Condition 队列(调用 await 的线程) ││
│ │ Node-X ← Node-Y ← Node-Z ││
│ │ WAITING ││
│ └─────────────────────────────────────┘│
│ │
│ await() → 释放锁,加入 Condition 队列 │
│ signal() → 从 Condition 移到 CLH 队列 │
│ unlock() → 唤醒 CLH 队列头节点 │
└─────────────────────────────────────────┘
3.2 代码示例
java
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.ReentrantLock;
public class ReentrantLockCondition {
private final ReentrantLock lock = new ReentrantLock();
private final Condition condition = lock.newCondition();
private boolean flag = false;
// 生产者
class Producer extends Thread {
@Override
public void run() {
lock.lock(); // 获取锁
try {
while (flag) {
condition.await(); // 1. 释放锁 2. 加入 Condition 队列等待
}
System.out.println("生产数据");
flag = true;
condition.signalAll(); // 唤醒 Condition 队列,移到 CLH 队列
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
lock.unlock(); // 释放锁,唤醒 CLH 队列头节点
}
}
}
// 消费者
class Consumer extends Thread {
@Override
public void run() {
lock.lock();
try {
while (!flag) {
condition.await();
}
System.out.println("消费数据");
flag = false;
condition.signalAll();
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
}
}
3.3 AQS 源码
AbstractQueuedSynchronizer.ConditionObject.await()
java
public final void await() throws InterruptedException {
// 1. 检查中断
if (Thread.interrupted())
throw new InterruptedException();
// 2. 构造节点,加入 Condition 队列
Node node = addConditionWaiter();
// 3. 【关键】释放锁!
int savedState = fullyRelease(node); // 释放 state,唤醒 CLH 后继
// 4. 线程状态改为 WAITING,park 阻塞
while (!isOnSyncQueue(node)) { // 检查是否在 CLH 队列(signal 会移过来)
LockSupport.park(this); // 阻塞
// 被唤醒后检查中断
if ((interruptMode = checkInterruptWhileWaiting(node)) != 0)
break;
}
// 5. 在 CLH 队列中竞争锁
if (acquireQueued(node, savedState) && interruptMode != THROW_IE)
selfInterrupt();
// 6. 清理取消的节点
if (node.nextWaiter != null)
unlinkCancelledWaiters();
}
AbstractQueuedSynchronizer.ConditionObject.signal()
java
public final void signal() {
// 1. 检查锁持有者
if (!isHeldExclusively())
throw new IllegalMonitorStateException();
// 2. 取 Condition 队列第一个节点
Node first = firstWaiter;
if (first != null)
doSignal(first); // 移到 CLH 队列
}
private void doSignal(Node first) {
do {
// 从 Condition 队列移除
if ((firstWaiter = first.nextWaiter) == null)
lastWaiter = null;
first.nextWaiter = null;
// 【关键】转移到 CLH 队列!
// 不是直接给锁,而是让节点去 CLH 排队竞争
} while (!transferForSignal(first) && (first = firstWaiter) != null);
}
final boolean transferForSignal(Node node) {
// CAS 修改节点状态从 CONDITION 到 0
if (!compareAndSetWaitStatus(node, Node.CONDITION, 0))
return false;
// 加入 CLH 队列尾部,返回前驱节点
Node p = enq(node);
int ws = p.waitStatus;
// 如果前驱是取消状态或 CAS 设置 SIGNAL 失败,直接唤醒
if (ws > 0 || !compareAndSetWaitStatus(p, ws, Node.SIGNAL))
LockSupport.unpark(node.thread); // 唤醒,让它去竞争锁
return true;
}
四、LockSupport 的许可机制
4.1 核心设计
┌─────────────────────────────────────────┐
│ LockSupport (底层原语) │
│ │
│ 每个线程有一个 ParkEvent(C++ 对象) │
│ ┌─────────────────────────────────────┐│
│ │ _event: 许可(permit) ││
│ │ 0 = 无许可(park 会阻塞) ││
│ │ 1 = 有许可(park 直接通过) ││
│ │ ││
│ │ _interrupted: 中断标志 ││
│ │ 0 = 未中断 ││
│ │ 1 = 已中断(park 直接返回) ││
│ └─────────────────────────────────────┘│
│ │
│ park(): │
│ 1. 检查许可,有则消费(1→0)直接返回 │
│ 2. 检查中断,有则直接返回 │
│ 3. 调用 OS 阻塞(Linux: futex) │
│ │
│ unpark(thread): │
│ 1. 设置许可(0→1) │
│ 2. 如果线程在阻塞,唤醒它 │
│ │
│ 特点: │
│ - 不持有锁,和锁无关 │
│ - 许可最多只有 1 个(多次 unpark = 一次)│
│ - 先 unpark 后 park,许可会被消费直接通过 │
└─────────────────────────────────────────┘
4.2 代码示例
java
import java.util.concurrent.locks.LockSupport;
public class LockSupportDemo {
public static void main(String[] args) {
Thread t = new Thread(() -> {
System.out.println("线程开始,准备 park");
LockSupport.park(); // 阻塞,等待 unpark 或中断
System.out.println("线程被唤醒!中断标志:" +
Thread.currentThread().isInterrupted());
});
t.start();
try { Thread.sleep(1000); } catch (InterruptedException e) {}
System.out.println("主线程 unpark");
LockSupport.unpark(t); // 唤醒 t 线程
}
}
4.3 先 unpark 后 park 的"漏洞"
java
public class ParkPermitDemo {
public static void main(String[] args) {
Thread t = new Thread(() -> {
try { Thread.sleep(1000); } catch (InterruptedException e) {}
// 此时主线程已经 unpark 过了
LockSupport.park(); // 不会阻塞!因为许可还在!
System.out.println("直接通过了!");
});
t.start();
// 先 unpark(线程还没 park)
LockSupport.unpark(t);
System.out.println("先 unpark 了");
}
}
输出:
先 unpark 了
直接通过了!
五、底层联系:都依赖 LockSupport
┌─────────────────────────────────────────────────────────────┐
│ 底层都依赖 LockSupport │
├─────────────────────────────────────────────────────────────┤
│ │
│ synchronized (ObjectMonitor) │
│ wait() → ObjectMonitor::wait() → _ParkEvent->park() │
│ notify() → ObjectMonitor::notify() → _ParkEvent->unpark()│
│ │
│ ReentrantLock (AQS) │
│ await() → AQS::await() → LockSupport.park(this) │
│ signal() → AQS::signal() → LockSupport.unpark(node.thread)│
│ │
│ LockSupport(最底层) │
│ park() → Unsafe.park(false, 0) → OS 阻塞(Linux futex) │
│ unpark() → Unsafe.unpark(thread) → OS 唤醒 │
│ │
└─────────────────────────────────────────────────────────────┘
C++ 底层实现
src/hotspot/share/runtime/park.cpp
cpp
class ParkEvent : public os::PlatformEvent {
private:
volatile int _event; // 许可:0=无,1=有
volatile int _interrupted; // 中断标志
public:
// park:阻塞当前线程
void park() {
// 1. 检查是否有许可(先消费许可)
if (Atomic::xchg(0, &_event) > 0) {
return; // 有许可,直接返回
}
// 2. 检查中断状态
if (_interrupted != 0) {
_interrupted = 0;
return; // 被中断,直接返回
}
// 3. 调用 OS 阻塞
ThreadBlockInVM tbivm(JavaThread::current());
os::PlatformEvent::park(); // Linux: futex
// 4. 被唤醒后,清除事件状态
_event = 0;
}
// unpark:给许可,唤醒线程
void unpark() {
// 1. 设置许可
if (Atomic::xchg(1, &_event) >= 0) {
return; // 之前已有许可,不需要唤醒
}
// 2. 唤醒 OS 层面的等待
os::PlatformEvent::unpark();
}
// 中断处理
void interrupt() {
_interrupted = 1; // 设置中断标志
unpark(); // 唤醒线程
}
};
六、经典面试题解析
Q1:wait() 为什么要放在 while 里而不是 if?
java
// ❌ 错误:用 if
if (!flag) {
lock.wait(); // 被唤醒后直接往下执行
}
// 问题:可能存在"虚假唤醒"(spurious wakeup)
// 唤醒时 flag 可能仍然不满足条件
// ✅ 正确:用 while
while (!flag) {
lock.wait(); // 被唤醒后回到循环开头,重新检查条件
}
// 安全:即使虚假唤醒,也会再次检查条件
Q2:notify() 和 notifyAll() 的区别?
| 方法 | 行为 | 适用场景 |
|---|---|---|
notify() |
从 _WaitSet 移一个 线程到 _EntryList |
只有一个等待线程,或只需要唤醒一个 |
notifyAll() |
从 _WaitSet 移所有 线程到 _EntryList |
多个等待线程,条件变化影响所有人 |
注意 :移到 _EntryList 后还要竞争锁,不是直接执行!
Q3:await() 和 wait() 的核心区别?
| 特性 | wait() |
await() |
|---|---|---|
| 所属类 | Object |
Condition(AQS 内部类) |
| 锁类型 | synchronized |
ReentrantLock |
| 队列数量 | 每个对象一个 _WaitSet |
一把锁可以创建多个 Condition |
| 灵活性 | 低 | 高(精准唤醒某类等待线程) |
Q4:LockSupport 为什么不会死锁?
LockSupport.park/unpark 的特点:
1. 不持有任何锁
2. 和 synchronized、ReentrantLock 无关
3. 只是线程状态的切换(RUNNABLE ↔ WAITING)
4. 即使先 unpark 后 park,许可机制保证不会永久阻塞
Q5:三种机制如何选择?
| 场景 | 推荐机制 | 原因 |
|---|---|---|
| 简单同步 | synchronized |
语法简洁,JVM 优化成熟 |
| 需要多个等待队列 | ReentrantLock + Condition |
一把锁多个 Condition,精准控制 |
| 构建同步工具 | LockSupport |
最底层,灵活构建自定义同步器 |
| 阻塞/唤醒线程 | LockSupport |
不依赖锁,直接使用 |
参考源码路径
src/hotspot/share/runtime/objectMonitor.cpp # wait/notify 实现
src/hotspot/share/runtime/objectMonitor.hpp # ObjectMonitor 结构
src/java.base/share/classes/java/util/concurrent/locks/AbstractQueuedSynchronizer.java # AQS
src/java.base/share/classes/java/util/concurrent/locks/LockSupport.java # LockSupport API
src/hotspot/share/runtime/park.cpp # ParkEvent 底层实现