面试复盘:synchronized 锁与 ReentrantLock 锁的区别及 AQS 认知完善

面试复盘:synchronized 锁与 ReentrantLock 锁的区别及 AQS 认知完善

在最近的一次面试中,面试官问了我一个并发相关的问题:"synchronized 锁和 ReentrantLock 锁有什么区别?"当时我给出了基础的回答,但脑海中浮现了 AQS(AbstractQueuedSynchronizer)的概念。由于对 AQS 了解有限,我没敢深入延伸。现在通过复盘和查阅资料,我希望完善这块知识,同时建构一个结构化的表达话术,以便未来面试中更自信地回答类似问题。

一、面试时的回答与反思

1. 我的回答

当时我的回答是这样的:

"synchronized 是 Java 的关键字,属于内置锁,简单易用,由 JVM 管理锁的获取和释放。ReentrantLockjava.util.concurrent.locks 包下的类,属于显式的锁,需要手动加锁和释放锁,功能更灵活。比如,ReentrantLock 支持公平锁和非公平锁的选择,还能响应中断、设置等待时间。而 synchronized 不支持这些特性,使用起来更简单,但灵活性较低。"

2. 反思

  • 优点 :我提到了两者的基本区别,比如内置 vs 显式、简单 vs 灵活,也点出了 ReentrantLock 的几个特性(公平性、中断、超时)。
  • 不足
    1. 回答比较表面,没能深入到实现原理,比如锁的底层机制。
    2. 想到 ReentrantLock 基于 AQS,但因不熟悉没敢展开,导致回答深度不够。
    3. 表达不够结构化,显得零散,没有清晰的逻辑框架。

3. 改进目标

通过复盘,我希望:

  • 补充 AQS 的知识,完善对 ReentrantLock 底层实现的理解。
  • 设计一个结构化的表达话术,逻辑清晰、层次分明。

二、synchronized 与 ReentrantLock 的区别(完善版)

经过查阅资料,我将两者的区别总结为以下几个维度,并融入 AQS 的理解:

1. 使用方式

  • synchronized

    • 关键字,内置于 JVM,直接修饰代码块或方法。

    • 加锁和释放由 JVM 自动管理,无需手动操作。

    • 示例:

      java 复制代码
      synchronized (obj) {
          // 同步代码块
      }
  • ReentrantLock

    • 显示锁类,需要手动调用 lock()unlock()

    • 释放锁必须放在 finally 块中,避免异常导致锁未释放。

    • 示例:

      java 复制代码
      ReentrantLock lock = new ReentrantLock();
      lock.lock();
      try {
          // 同步代码块
      } finally {
          lock.unlock();
      }

2. 功能特性

  • synchronized
    • 只支持非公平锁(先到先得)。
    • 不支持中断或超时等待。
    • 简单直接,适合基本同步场景。
  • ReentrantLock
    • 可选择公平锁(new ReentrantLock(true))或非公平锁。
    • 支持中断(lockInterruptibly())、超时(tryLock(long, TimeUnit))。
    • 提供条件变量(Condition),支持更复杂的同步逻辑。

3. 性能

  • synchronized
    • JDK 1.6 后优化显著,引入偏向锁、轻量级锁、重量级锁,性能接近 ReentrantLock
    • 但在高竞争场景下,仍依赖操作系统内核态的互斥锁(Mutex),开销较大。
  • ReentrantLock
    • 基于 AQS 实现,通过 CAS(Compare-And-Swap)和自旋锁在用户态完成大部分操作,减少内核态切换。
    • 在高并发场景下,性能可能略优于 synchronized

4. 底层实现

  • synchronized
    • 由 JVM 实现,依赖对象头中的 Monitor(监视器锁)。
    • 通过 monitorentermonitorexit 字节码指令控制。
  • ReentrantLock
    • 基于 AQS 框架,内部维护一个 volatile 状态变量(state)和等待队列。
    • 加锁通过 CAS 修改 state,失败则进入 AQS 队列等待。

5. 可重入性

  • 两者都支持可重入性,即同一线程可多次获取同一把锁,不会自我阻塞。

三、AQS 的简单认知

ReentrantLock 的底层依赖 AQS(AbstractQueuedSynchronizer),这是 Java 并发包的核心框架。以下是我通过查阅资料整理的 AQS 基础:

  • 核心思想
    • AQS 维护一个 volatile int state(表示锁状态)和一个双向链表队列(存放等待线程)。
    • state = 0 表示锁未被占用,state > 0 表示已加锁(支持重入,值递增)。
  • 加锁过程
    • 通过 CAS 尝试将 state 从 0 改为 1,成功则获取锁。
    • 失败则将线程加入等待队列,进入阻塞状态。
  • 释放锁
    • state 减 1,若减到 0,则唤醒队列中的下一个线程。
  • 与 ReentrantLock 的关系
    • ReentrantLock 通过继承 AQS 的 Sync 类实现锁逻辑。
    • 公平锁按队列顺序唤醒,非公平锁允许插队。

由于时间有限,我目前对 AQS 的理解停留在概览层面,后续需深入学习其源码(如 tryAcquiretryRelease 方法)。

四、结构化的表达话术设计

基于以上分析,我设计了以下面试表达话术,力求逻辑清晰、内容全面:

"面试官您好,关于 synchronizedReentrantLock 的区别,我可以从以下几个方面来回答:
第一,使用方式synchronized 是 JVM 内置的关键字,自动加锁和释放,用法简单;而 ReentrantLock 是 JUC 包中的类,需要手动调用 lock()unlock(),更灵活但要求开发者注意释放锁。
第二,功能特性synchronized 只支持非公平锁,且不支持中断或超时;ReentrantLock 则提供公平/非公平锁选项,还支持中断、超时等待,甚至可以用 Condition 实现复杂同步。
第三,性能表现 :早期 synchronized 性能较差,但 JDK 1.6 后通过偏向锁和轻量级锁优化,已接近 ReentrantLock。不过在高竞争场景下,ReentrantLock 因基于 AQS 和 CAS,减少了内核态切换,可能更有优势。
第四,底层实现synchronized 依赖 JVM 的 Monitor,由对象头控制;ReentrantLock 基于 AQS,通过 state 变量和等待队列管理锁状态。

如果要延伸,ReentrantLock 的 AQS 是一个通用同步框架,核心是维护一个状态值和线程队列,通过 CAS 实现高效加锁。我对 AQS 了解还不深入,但知道它是许多 JUC 工具(如 CountDownLatch)的基础,值得进一步学习。"

五、总结与计划

这次面试暴露了我对 AQS 的知识盲点,虽然基础回答没出错,但缺乏深度让我错失了展示能力的机会。通过复盘,我不仅完善了两者的区别,还初步理解了 AQS 的作用。未来计划:

  1. 阅读 ReentrantLock 和 AQS 的源码,掌握其实现细节。
  2. 练习结构化表达,提升回答的逻辑性和流畅性。
相关推荐
Victor3569 分钟前
MongoDB(87)如何使用GridFS?
后端
Victor35612 分钟前
MongoDB(88)如何进行数据迁移?
后端
小红的布丁29 分钟前
单线程 Redis 的高性能之道
redis·后端
GetcharZp34 分钟前
Go 语言只能写后端?这款 2D 游戏引擎刷新你的认知!
后端
宁瑶琴2 小时前
COBOL语言的云计算
开发语言·后端·golang
普通网友2 小时前
阿里云国际版服务器,真的是学生党的性价比之选吗?
后端·python·阿里云·flask·云计算
IT_陈寒3 小时前
Vue的这个响应式问题,坑了我整整两小时
前端·人工智能·后端
Soofjan4 小时前
Go 内存回收-GC 源码1-触发与阶段
后端
shining4 小时前
[Golang]Eino探索之旅-初窥门径
后端
掘金者阿豪4 小时前
Mac 程序员效率神器:6 个我每天都在用的 Mac 工具推荐(Alfred / Paste / PixPin / HexHub / iTerm2 /)
后端