Java锁机制全解析:从偏向锁到重量级锁

发布时间:2026/6/27 18:50:47
Java锁机制全解析:从偏向锁到重量级锁 在 Java 并发编程中,锁的性能优化一直是 JVM 工程师的核心课题。从 JDK 1.6 引入的偏向锁、轻量级锁,到 JDK 15 彻底废除偏向锁,JVM 的锁优化策略经历了十多年的演进。本文将深度解析 Java 锁的升级机制、底层实现原理,以及偏向锁被废除背后的技术原因。一、Java 锁的底层实现:synchronized 的本质1.1 为什么需要锁优化?synchronized关键字在 JDK 1.6 之前被称为"重量级锁"——每次获取锁都需要通过操作系统 Mutex 调用,涉及用户态到内核态的切换,成本极高。但实际开发中发现:大多数锁不存在多线程竞争:单线程反复进入同一块同步代码是常态竞争通常只持续很短时间:线程持有锁的时间往往很短JVM 团队基于这两个观察,提出了锁升级的优化策略:无锁 → 偏向锁 → 轻量级锁 → 重量级锁,只在真正需要时才使用昂贵的操作系统互斥量。1.2 对象头中的 Mark WordJava 对象的锁状态完全存储在对象头的Mark Word中(64位 JVM):|----------------------------------------------------------------------------------| | 锁状态 | 62位内容分布(64位 JVM) | 标志位 | |----------------------------------------------------------------------------------| | 无锁 | unused:25 | hashCode:31 | unused:1 | age:4 | biased_lock:0 | 01 | | 偏向锁 | threadID:54 | epoch:2 | unused:1 | age:4 | biased_lock:1 | 01 | | 轻量级锁 | ptr_to_lock_record:62 | 00 | | 重量级锁 | ptr_to_heavyweight_monitor:62 | 10 | | GC 标记 | 空 | 11 | |----------------------------------------------------------------------------------|关键信息:锁标志位(2 bit):01表示无锁/偏向锁,00轻量级锁,10重量级锁biased_lock(1 bit):1表示偏向锁模式已启用threadID:记录偏向的线程 ID二、锁的四种状态详解2.1 无锁状态对象刚创建时,处于无锁状态。如果biased_lock位为 0,且锁标志位为01,则对象没有启用偏向锁。// 验证对象头结构importorg.openjdk.jol.info.ClassLayout;publicclassLockStateDemo{publicstaticvoidmain(String[]args){Objectobj=newObject();// 打印对象布局(需要 JOL 工具依赖)System.out.println(ClassLayout.parseInstance(obj).toPrintable());}}输出示例(64 位 JVM,开启压缩指针):java.lang.Object object internals: OFFSET SIZE TYPE DESCRIPTION VALUE 0 4 (object header) 01 00 00 00 // 无锁状态,标志位 01 4 4 (object header) 00 00 00 00 8 4 (object header) e5 01 00 f8 // Klass 指针 12 4 (loss due to the next object alignment) Instance size: 16 bytes2.2 偏向锁:一锁定终身核心思想:假设锁只被一个线程使用,直接在对象头中记录该线程 ID,后续该线程进入同步块时无需任何 CAS 操作。加锁过程:检查Mark Word是否可偏向(biased_lock: 1)检查是否是当前线程 ID如果是,直接获得锁(无需 CAS!)如果不是,尝试 CAS 替换线程 IDpublicclassBiasedLockDemo{staticObjectlock=newObject();publicstaticvoidmain(String[]args)throwsException{// 启动时等待几秒,确保偏向锁已启动(JVM 启动有延迟)Thread.sleep(5000);Objectobj=newObject();// 打印对象头:此时应该是可偏向但未偏向状态System.out.println("=== 创建后 ===");System.out.println(ClassLayout.parseInstance(obj).toPrintable());synchronized(obj){ /