synchronized 方法能同步是因为编译器自动插入 monitorenter/monitorexit 指令,触发 ObjectMonitor 的获取与释放;ObjectMonitor 是 C++ 实现的结构体,含 _owner、_EntryList、_WaitSet 等字段,支持重入和等待队列;锁升级为偏向→轻量级→重量级,JDK 15+ 默认关闭偏向锁。
Java 中 synchronized 不是靠 JVM 解释执行的“语法糖”,而是直接绑定到操作系统级的互斥原语,底层依赖的是 monitorenter / monitorexit 字节码指令 + HotSpot 的 ObjectMonitor 机制。
为什么 synchronized 方法没有显式加锁却能同步?
因为编译器会把 synchronized 方法自动转换为在方法入口插入 monitorenter 指令、出口插入 monitorexit 指令。JVM 执行时,这两条指令会触发对当前对象(或 class 对象)关联的 ObjectMonitor 的获取与释放。
关键点:
-
ObjectMonitor是 C++ 实现的,每个 Java 对象头(mark word)里存着指向它的指针(偏向锁被撤销后) - 非静态方法锁的是
this对象;静态方法锁的是Class对象(即MyClass.class) - 如果方法抛异常,JVM 仍会保证
monitorexit执行 —— 这就是 synchronized 不需要手动 try-finally 的原因
锁升级过程:偏向锁 → 轻量级锁 → 重量级锁
HotSpot 默认开启锁优化,实际锁状态在对象头 mark word 中动态变化,不是固定一种实现:
立即学习“Java免费学习笔记(深入)”;
- 偏向锁:假设无竞争,直接把线程 ID 记录在 mark word 中;后续同一线程重入无需 CAS
-
轻量级锁:有竞争但不激烈时,用 CAS 尝试将对象头指向线程栈中的
Lock Record;失败则升级 -
重量级锁:真正调用操作系统 mutex,挂起线程(
pthread_mutex_lock),此时 mark word 存的是ObjectMonitor*地址
锁升级不可逆(但 JDK 10+ 引入了锁降级尝试,仅限 GC 阶段,生产环境基本不生效)。