一聚教程网:一个值得你收藏的教程网站

最新下载

热门教程

Java 中 ReentrantLock 获取锁时的阻塞唤醒机制原理

时间:2026-07-13 09:10:46 编辑:袖梨 来源:一聚教程网

ReentrantLock 的阻塞与唤醒基于 AQS + LockSupport.park/unpark:获取失败时线程入 CLH 队列并 park() 暂停;释放锁时 unpark 队首线程使其重试竞争,非公平锁允许插队,全程规避 wait/notify 限制。

ReentrantLock 获取锁失败时的阻塞与唤醒,核心靠的是 AQS(AbstractQueuedSynchronizer) + LockSupport.park()/unpark() 这套组合,不是传统的 wait/notify。它更轻量、更灵活,也规避了 wait/notify 的诸多限制。

阻塞:线程怎么停下来等锁?

当线程调用 lock() 但锁已被占用,且不是自己重入时,AQS 会把该线程封装成一个 Node 节点,加入内部的 CLH 同步队列尾部,并执行 LockSupport.park() 让其暂停执行。

  • park() 是 JVM 层提供的底层阻塞原语,不依赖 synchronized,也不要求在同步块中调用
  • 被 park 的线程状态变为 WAITING,不消耗 CPU,也不会响应中断(除非用 lockInterruptibly()
  • 每个线程都有一个私有的“许可”(permit),park() 会消费这个 permit;如果没有 permit,线程就阻塞住

唤醒:谁来叫醒它?什么时候?

锁释放时(unlock()),AQS 会从同步队列头开始,尝试唤醒下一个等待节点——调用 LockSupport.unpark(thead) 给对应线程发放一个 permit,使其恢复运行。

  • 唤醒动作由释放锁的线程完成,不是锁本身“主动通知”,而是 AQS 按队列顺序逐个 unpark
  • 被 unpark 的线程重新进入锁竞争逻辑(tryAcquire()),成功则获取锁继续执行;失败则可能再次 park(比如公平锁下还有前序节点)
  • 非公平锁允许新来的线程“插队”竞争,所以刚 unpark 的线程不一定立刻拿到锁

和 wait/notify 的关键区别在哪?

这套机制绕开了 Object 监视器模型的约束,解决了几个经典痛点:

立即学习“Java免费学习笔记(深入)”;

  • 不用写在 synchronized 块里 —— park/unpark 可以在任意位置调用
  • 不需要持有某个对象的 monitor —— 不再绑定特定对象实例
  • 不会因虚假唤醒而意外返回 —— park() 只有收到 unpark 或被中断才会返回
  • 支持精准唤醒 —— unpark(Thread t) 可指定唤醒哪个线程,不像 notify() 随机选一个

公平性如何影响唤醒行为?

公平锁和非公平锁在唤醒路径上差异明显:

  • 公平锁:严格按 CLH 队列顺序唤醒,先到先得,保证 FIFO
  • 非公平锁:释放锁后,先让新来的线程抢一次(CAS 尝试),抢不到才去唤醒队列头节点;这可能导致“插队”,提升吞吐但牺牲公平性
  • 无论哪种,唤醒动作都只作用于队列头部节点,AQS 不会跳过中间节点

热门栏目