《Java 100 天进阶之路》第52篇:synchronized深度解析(2026版) 📅 2026/7/8 2:34:09 第52篇synchronized深度解析2026版系列导航《Java 100 天进阶之路》完整目录 |⬅️ 上一篇第51篇线程生命周期与创建方式 |➡️ 下一篇第53篇volatile与JMM待发布️ 本文阅读地图3 分钟速览第51篇学会了创建线程本篇深入线程安全的核心——synchronized。从对象头到锁升级从字节码到JVM优化彻底搞懂这把“万能锁”模块核心问题一句话回答三种用法synchronized 怎么用修饰实例方法锁this、静态方法锁Class、代码块自定义锁对象底层原理锁存在哪里Java对象头的Mark Word中64位JVM占8字节锁升级性能怎么优化无锁 → 偏向锁 → 轻量级锁 → 重量级锁逐级升级不可逆JVM优化还有哪些优化手段自适应自旋锁、锁消除、锁粗化面试最爱问高频考点有哪些见文末 小节文章目录第52篇synchronized深度解析2026版️ 本文阅读地图3 分钟速览一、核心知识点二、生活类比从“公共厕所”到“VIP 厕所”三、Java 对象布局与 Mark Word四、锁升级全流程4.1 无锁 → 偏向锁4.2 偏向锁 → 轻量级锁4.3 轻量级锁 → 重量级锁4.4 锁升级流程总结五、可重入性原理六、JVM 对 synchronized 的优化6.1 自适应自旋锁6.2 锁消除Lock Elimination6.3 锁粗化Lock Coarsening七、synchronized vs ReentrantLock八、生产级避坑清单九、面试高频考点 面试官追问陷阱加分题十、练习题 你的学习进度 下一篇文章预告一、核心知识点synchronized 是什么synchronized 是 Java 内置的互斥同步锁基于 JVM 实现保证多线程环境下共享资源的原子性、可见性与有序性同时具备自动释放锁、可重入的特性。三种使用方式使用方式锁对象作用范围修饰实例方法当前实例this同一实例的所有同步方法互斥修饰静态方法当前类的Class对象该类所有实例的静态同步方法互斥修饰代码块括号内指定的对象锁粒度最细灵活性最高锁升级路径JDK 1.6无锁 → 偏向锁 → 轻量级锁 → 重量级锁单向升级不可逆JDK 1.6 之前 synchronized 只有重量级锁性能差。JDK 1.6 引入锁升级机制根据竞争程度动态调整锁形态。JDK 15 默认禁用偏向锁-XX:-UseBiasedLocking但仍需理解其原理。JVM 优化手段自适应自旋锁JVM 自动统计锁竞争情况决定自旋等待还是阻塞锁消除JIT 通过逃逸分析检测到不可能存在竞争时直接移除锁锁粗化将多个连续的加锁/解锁操作合并为一次大范围加锁二、生活类比从“公共厕所”到“VIP 厕所”synchronized 就像一个公共厕所厕所只有一间锁对象门口挂着一个“占用/空闲”指示牌Mark Word。有人进去了牌子翻到“占用”获取锁其他人只能在门口排队BLOCKED。里面的人出来牌子翻回“空闲”释放锁排队的人抢着进锁竞争。锁升级就像厕所管理的优化无锁厕所空着谁都能进但没人进。偏向锁VIP 厕所社区给固定住户配了一把专属钥匙偏向线程 ID每天都是同一个人用省去了每次翻牌子的麻烦。轻量级锁自旋等待偶尔有客人来客人不直接排队而是在门口原地转圈等待自旋期望里面的人很快出来。重量级锁正式排队客人越来越多原地转圈太耗体力社区正式安排保安维持秩序排队的人进入等待队列OS 线程阻塞。三、Java 对象布局与 Mark Wordsynchronized 的锁状态完全存储在 Java 对象头的Mark Word中。在 64 位 JVM 中Java 对象的内存布局分为三部分对象头Object HeaderMark Word8 字节 Klass Pointer4 字节压缩后实例数据Instance Data成员变量对齐填充Padding保证对象大小是 8 的倍数Mark Word 是锁的核心载体锁状态锁标志位2bit偏向标志1bitMark Word 存储内容无锁不可偏向010对象 hashCode 分代年龄无锁可偏向011空可存储偏向线程 ID偏向锁011偏向线程 ID54bit epoch2bit 分代年龄4bit轻量级锁00—指向线程栈中 Lock Record 的指针62bit重量级锁10—指向 ObjectMonitor 对象的指针62bitGC 标记11—空关键判断仅靠锁标志位无法区分无锁和偏向锁都是 01需配合偏向标志位biased_lock一起判断。四、锁升级全流程4.1 无锁 → 偏向锁触发条件对象刚创建尚未被任何线程锁定。获取流程线程尝试获取锁检查 Mark Word 是否处于可偏向状态标志位 01 偏向标志 0通过 CAS 将当前线程 ID 写入 Mark WordCAS 成功 → 进入偏向锁状态Mark Word 存储该线程 IDCAS 失败 → 说明有其他线程竞争撤销偏向锁升级为轻量级锁偏向锁的核心思想如果一个线程反复获取同一把锁每次都要 CAS 操作也是有开销的。偏向锁让该线程“记住”自己下次直接进入省去 CAS。释放偏向锁不会主动释放。当其他线程尝试获取时触发偏向撤销。4.2 偏向锁 → 轻量级锁触发条件另一个线程尝试获取已被偏向的锁。升级流程线程 B 尝试获取锁发现 Mark Word 中的线程 ID 不是自己触发偏向撤销JVM 暂停持有锁的线程 A到达安全点检查线程 A 是否存活、是否还在同步块中若线程 A 已退出同步块撤销偏向恢复为无锁状态再 CAS 竞争若线程 A 仍在同步块中升级为轻量级锁轻量级锁的实现线程 B 在自己的栈帧中创建一个Lock Record锁记录通过 CAS 将 Mark Word 替换为指向 Lock Record 的指针CAS 成功 → 线程 B 获取轻量级锁CAS 失败 → 说明有竞争自旋等待4.3 轻量级锁 → 重量级锁触发条件自旋次数超过阈值JVM 自适应或另一个线程也来竞争。升级流程轻量级锁 CAS 替换 Mark Word 失败说明存在锁竞争JVM 将锁膨胀为重量级锁Mark Word 替换为指向ObjectMonitor的指针竞争失败的线程进入_EntryList阻塞等待ObjectMonitor 核心结构ObjectMonitor { _owner // 持有锁的线程 _count // 锁计数器可重入 _recursions // 重入次数 _EntryList // 等待获取锁的线程队列 _WaitSet // 调用 wait() 的线程队列 }4.4 锁升级流程总结五、可重入性原理synchronized 是可重入锁——同一个线程可以多次获取同一把锁。实现原理每个对象关联一个ObjectMonitor内部维护_count计数器线程第一次获取锁_owner 当前线程_count 1同一线程再次进入_count不阻塞退出同步块_count--_count 0时真正释放锁publicsynchronizedvoidmethodA(){methodB();// 同一把锁可重入}publicsynchronizedvoidmethodB(){// 直接进入不需要重新竞争}六、JVM 对 synchronized 的优化6.1 自适应自旋锁线程尝试获取轻量级锁失败时不立即阻塞而是原地循环等待自旋JVM 会统计每个锁的自旋成功率动态调整自旋次数成功率高 → 多自旋一会儿成功率低 → 少自旋或直接阻塞6.2 锁消除Lock EliminationJIT 通过逃逸分析判断锁对象是否只被单个线程访问如果不存在竞争JVM 直接移除 synchronized示例StringBuffer的方法都是 synchronized但在局部变量中只被单线程访问时JVM 会消除锁6.3 锁粗化Lock Coarsening将多个连续的加锁/解锁操作合并成一次大范围加锁减少锁获取/释放的次数降低开销示例循环内反复加锁 → JVM 把锁移到循环外七、synchronized vs ReentrantLock对比维度synchronizedReentrantLock实现层级JVM 内置关键字JDK 提供的 API 类锁释放自动释放异常也释放需在 finally 中手动 unlock()可重入性✅ 支持✅ 支持公平锁❌ 非公平支持公平/非公平中断响应❌ 不可中断✅lockInterruptibly()超时获取❌ 不支持✅tryLock(timeout)条件变量wait()/notify()Condition多个性能低竞争时接近高竞争时略逊高竞争时略优锁升级✅ 有偏向→轻量→重量❌ 无适用场景简单同步低/中竞争高级并发控制高竞争八、生产级避坑清单✅ synchronized 使用避坑指南 1. 锁对象不能用 null → 抛 NullPointerException 2. 锁对象不能用基本类型 → 只能用引用类型 3. 不要用 String 常量作为锁对象 → 不同模块可能共用同一字符串常量池导致意外死锁 4. 不要在锁内执行耗时操作 → 长时间持有锁阻塞其他线程 5. 注意锁粒度 → 能锁代码块就别锁整个方法 6. wait() 必须在 synchronized 块中调用 → 否则抛 IllegalMonitorStateException 7. 虚拟线程中慎用 synchronized → 会导致 Pinning固定在载体线程建议用 ReentrantLock九、面试高频考点Q1synchronized 的三种使用方式锁对象分别是什么① 修饰实例方法 → 锁当前实例this② 修饰静态方法 → 锁当前类的Class对象③ 修饰代码块 → 锁括号内指定的对象。Q2synchronized 的锁存在哪里存在 Java 对象头的Mark Word中。64 位 JVM 中 Mark Word 占 8 字节存储锁状态、线程 ID、hashCode 等信息。Q3synchronized 锁升级的过程从低到高依次为无锁 → 偏向锁 → 轻量级锁 → 重量级锁单向升级不可逆。JDK 1.6 引入JDK 15 默认禁用偏向锁。Q4偏向锁、轻量级锁、重量级锁分别解决什么问题偏向锁针对无竞争场景避免 CAS 开销轻量级锁针对轻度竞争通过自旋避免线程阻塞重量级锁针对重度竞争通过 OS 互斥量阻塞线程。Q5synchronized 的可重入性是如何实现的每个对象关联的ObjectMonitor维护_count计数器。同一线程重复获取时_count退出时_count--归零时释放锁。Q6JVM 对 synchronized 做了哪些优化①锁升级偏向→轻量→重量②自适应自旋锁动态调整自旋次数③锁消除逃逸分析移除无竞争锁④锁粗化合并连续加锁/解锁。 面试官追问陷阱加分题追问1“JDK 15 为什么默认禁用偏向锁” 偏向锁的维护成本撤销、安全点在大多数应用场景下超过了其带来的收益。现代应用通常有多个线程竞争偏向锁优势不明显。可通过-XX:UseBiasedLocking重新启用。追问2“轻量级锁一定比重量级锁快吗”不一定。轻量级锁通过自旋等待适用于锁持有时间短、竞争不激烈的场景。如果锁持有时间长或竞争激烈自旋会浪费 CPU此时重量级锁让线程阻塞反而更高效。JVM 的自适应自旋会根据历史统计动态决策。追问3“虚拟线程中为什么慎用 synchronized” 虚拟线程在synchronized块中会被固定在载体线程上Pinning无法被卸载失去轻量级优势。高并发场景建议用ReentrantLock替代。十、练习题分析题以下代码中两个线程分别调用methodA()和methodB()会互相阻塞吗为什么publicclassTest{publicsynchronizedvoidmethodA(){...}publicstaticsynchronizedvoidmethodB(){...}} 思路methodA()锁的是实例thismethodB()锁的是Test.class两者互不影响。代码题用 JOL 工具打印一个普通对象的对象头布局观察无锁状态下的 Mark Word。场景设计某系统使用 synchronized 保护共享资源并发量高时性能急剧下降。分析可能的原因和优化方案。 思路可能是锁粒度太大锁整个方法或锁持有时间过长。优化方案缩小锁粒度锁代码块、减少锁内操作、考虑使用 ReentrantLock 或 ConcurrentHashMap。 你的学习进度当前第52篇 / 共108篇 ·进阶篇并发编程与JUC详解第51~60篇✅ 已完成基础篇44篇 第45~52篇 正在学第52篇⏳ 待学习第53~108篇 完整目录 学习指南 | 订阅本专栏不错过每一篇 下一篇文章预告下一篇《第53篇volatile与JMM》内容简介Java 内存模型JMM、可见性、有序性、禁止指令重排、happens-before 原则、volatile 与 synchronized 的区别。多线程专题持续深入拿下 JMM 核心《Java 100 天进阶之路 | 从入门到上岗就业》每天一篇建议收藏 关注一起100天拿offer 点击关注我更新后第一时间收到推送