synchronized → volatile/JMM → CAS/AQS 追问链#
追问路径#
Q: synchronized的底层原理是什么?
→ 对象头Mark Word关联Monitor,靠monitorenter/monitorexit字节码;JDK6后引入锁升级避免直接用重量级锁
Q: 锁升级的过程是怎样的?
→ 无锁 → 偏向锁(Mark Word记录线程ID) → 轻量级锁(CAS自旋抢锁) → 重量级锁(Monitor阻塞),只升不降
Q: 偏向锁为什么在JDK15被废弃?
→ 撤销偏向要STW且维护成本高,现代应用普遍存在竞争收益下降;JDK15默认关闭、JDK18彻底移除
├─ Q: volatile解决什么问题?和synchronized有什么区别?
│ → volatile保证可见性+禁止重排序,但不保证原子性;synchronized三者都保证但开销更大
│ Q: volatile底层怎么保证可见性和有序性?
│ → 写操作插StoreStore/StoreLoad屏障、读插LoadLoad/LoadStore屏障;底层lock前缀指令触发MESI缓存一致性,强制刷主存
│ Q: 什么是happens-before?
│ → JMM定义的偏序关系:程序顺序/锁规则/volatile规则/传递性等8条,是判断跨线程可见性的语义依据,不等于真实时间先后
│ Q: 经典DCL单例为什么必须加volatile?
│ → new对象非原子(分配内存→初始化→赋引用),指令重排序可能让其他线程拿到引用非空但未初始化完成的半成品对象
└─ Q: CAS是什么?有哪些问题?
→ Compare-And-Swap,靠CPU的cmpxchg指令实现无锁原子更新;三大问题:ABA、自旋空转开销、只能保证单变量原子
Q: ABA和自旋开销怎么解决?
→ ABA加版本号(AtomicStampedReference);高并发自旋空转用LongAdder分段累加(base+Cell数组),@Contended避免伪共享
Q: AQS是怎么基于CAS构建锁的?
→ volatile int state表示同步状态 + CAS改state + CLH变体双向队列挂等待线程;ReentrantLock/CountDownLatch/Semaphore/ReentrantReadWriteLock都基于它
Q: ReentrantLock比synchronized强在哪?什么时候用?
→ 可中断/可超时tryLock/公平锁/绑定多个Condition;代价是必须finally手动unlock,简单互斥场景synchronized自动释放更安全plaintext涉及知识点#
- synchronized原理与锁升级 — Monitor与Mark Word的锁状态流转
- 偏向锁-轻量级锁-重量级锁 — 三级锁的升级条件与成本
- volatile原理 — 内存屏障与lock前缀指令
- JMM与happens-before — Java内存模型的可见性语义约定
- CAS原理与ABA问题 — 无锁编程的硬件基础与缺陷
- AQS原理 — JUC同步器的统一框架(state+CLH队列)
- ReentrantLock与synchronized对比 — 显式锁 vs 内置锁的取舍
- 原子类与LongAdder — CAS自旋优化与分段累加
- 伪共享与缓存行 — @Contended与缓存行填充
- 读写锁原理 — 读共享写独占的state高低位拆分
- CountDownLatch与CyclicBarrier — AQS共享模式的典型应用
- Semaphore使用场景 — 基于AQS的信号量限流
核心串联逻辑#
- synchronized锁升级:JDK6前直接用Monitor重量级锁(涉及用户态/内核态切换),优化后从偏向锁→轻量级锁(CAS)→重量级锁逐级升级,单线程或低竞争场景几乎零开销
- volatile三大语义:保证可见性(MESI)、禁止重排序(内存屏障),但复合操作(如i++)不原子
- JMM是契约:happens-before给出无需关心底层屏障就能推导可见性的高层规则,DCL靠volatile阻止”赋引用”重排到”初始化”前
- CAS到AQS的演进:CAS是单变量无锁原语 → LongAdder用分段把热点CAS打散 → AQS用
state+队列把CAS扩展成可阻塞的通用锁框架 - 代码示例:
java// DCL单例:volatile防止半初始化对象逃逸 public class Singleton { private static volatile Singleton INSTANCE; // 必须volatile public static Singleton get() { if (INSTANCE == null) { // 第一次检查,避免每次加锁 synchronized (Singleton.class) { if (INSTANCE == null) // 第二次检查,防重复创建 INSTANCE = new Singleton(); } } return INSTANCE; } }
面试回答串联#
30秒速答#
“synchronized靠对象头Mark Word关联Monitor,JDK6后做了偏向锁→轻量级锁→重量级锁的升级优化。volatile保证可见性和有序性但不保证原子性,靠内存屏障和lock指令实现,背后是JMM的happens-before语义。CAS是无锁原子操作但有ABA和自旋问题,AQS用volatile state加CLH队列把CAS封装成ReentrantLock这类可阻塞锁。“
2分钟展开答#
“synchronized底层是对象头Mark Word关联Monitor对象,通过monitorenter/monitorexit字节码加解锁。JDK6引入锁升级避免一上来就用重量级锁:无竞争时偏向锁记录线程ID,出现竞争升级为轻量级锁做CAS自旋,自旋失败再升级为重量级锁阻塞——只升不降。偏向锁在JDK15被废弃,因为撤销要STW、现代应用普遍有竞争。volatile保证可见性和禁止重排序但不保证原子性:写后插StoreLoad屏障、底层lock前缀指令触发MESI缓存一致性强制刷主存。可见性的理论基础是JMM的happens-before——它定义了程序顺序、锁、volatile、传递性等规则,是判断一个写对另一个读是否可见的依据。典型应用是DCL单例必须给instance加volatile,否则new对象的’分配内存→初始化→赋引用’三步可能重排序,导致其他线程拿到非空但未初始化的对象。无锁方向上,CAS靠cmpxchg指令实现,但有ABA(加版本号解决)、自旋空转(LongAdder分段累加+@Contended避免伪共享)、只能保单变量三个问题。AQS就是用一个volatile的state加CLH双向队列,把CAS扩展成可阻塞、可排队的通用同步框架,ReentrantLock、CountDownLatch、Semaphore都基于它。ReentrantLock相比synchronized支持可中断、超时tryLock、公平锁和多Condition,但要手动unlock。“
相关追问链#
- HashMap-ConcurrentHashMap-并发集合追问链 — ConcurrentHashMap用CAS+synchronized锁Node、size()用LongAdder思想
- 线程池-Spring异步-MQ消费追问链 — 线程池内部用AQS(Worker)和阻塞队列实现
- JVM-GC-内存泄漏-OOM追问链 — ThreadLocal的弱引用与内存泄漏
- 进程线程-调度-上下文切换-协程追问链 — 重量级锁阻塞引发的上下文切换开销