Java 线程等待唤醒机制深度解析:synchronized、ReentrantLock、LockSupport 底层实现对比

Java 线程等待唤醒机制深度解析:synchronized、ReentrantLock、LockSupport 底层实现对比

日期 :2026-05-15
标签 :Java并发、等待唤醒、ObjectMonitor、AQS、LockSupport、Condition、HotSpot源码
阅读建议:配合 OpenJDK 源码理解三种机制的底层联系与差异


目录

  1. 三种机制总览对比
  2. [synchronized 的等待唤醒](#synchronized 的等待唤醒)
  3. [ReentrantLock 的 Condition 机制](#ReentrantLock 的 Condition 机制)
  4. [LockSupport 的许可机制](#LockSupport 的许可机制)
  5. [底层联系:都依赖 LockSupport](#底层联系:都依赖 LockSupport)
  6. 经典面试题解析

一、三种机制总览对比

特性 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 底层实现
相关推荐
赤水无泪1 小时前
Qt 全模块汇总列表
开发语言·qt
yong99901 小时前
MATLAB仿真计算电磁波回波信号的技术路径与实现指南
开发语言·matlab
数字化顾问1 小时前
(122页PPT)企业数字化IT架构蓝图规划设计方案(附下载方式)
java·运维·架构
不是光头 强1 小时前
Spring Boot 多线程场景下 i18n 国际化失效问题排查与解决
java·开发语言·springboot
jieyucx1 小时前
Go 语言核心关键字:defer 深度解析与实战避坑
开发语言·后端·golang·defer
星恒随风1 小时前
四天学完前端基础三件套(JavaScript篇)
开发语言·前端·javascript·笔记
勿忘,瞬间2 小时前
Spring IOC and DI
java·spring
十五年专注C++开发2 小时前
TypePerf:Windows 命令行性能计数器工具(CPU利用率、内存利用率、GPU利用率等)
c++·windows·typeperf
小坏讲微服务2 小时前
SpringBoot4.0整合Spring Security+MyBatis Plus完整权限框架实现
java·spring·mybatis·spring security·mybatis plus·springboot4.0