轻量锁偏向锁重量锁害人不浅!synchronized源码!
环境
源码环境搭建:https://blog.csdn.net/qq_35040959/article/details/149060610?spm=1011.2124.3001.6209
源码版本:jdk24
oop(java每个对象的header结构)
源码位置: src/hotspot/share/oops/oop.hpp#L51-L60
源码:
class oopDesc {private:// 对象头 Mark Word - 使用 volatile 确保内存可见性volatile markWord _mark; // 占用 1 Word(8 字节/64位系统)// 类元数据联合体 - 通过指针压缩优化内存占用union _metadata {Klass* _klass; // 指向class类:普通指针(64位系统占8字节)narrowKlass _compressed_klass; // 压缩指针(32位占4字节)} _metadata; // 占用 1 Word(压缩时)或 2 Words(非压缩时)
}
对象头布局(内存布局)
对象头布局示例(64位系统):
[---------------------------------------------------------]
非压缩模式: [ markWord (8字节) | Klass* (8字节) ]
压缩模式: [ markWord (8字节) | narrowKlass (4字节) ]
markWord布局(记录对象基础信息)
- 初始状态(无锁):
[31位哈希码 | 4位年龄 | 3位移位 | 0b01]
- 轻量级锁竞争:
[64位栈锁记录地址 | 0b00]
- 锁膨胀过程:
[ObjectMonitor* | 0b10]
源码位置:src/hotspot/share/oops/markWord.hpp:51
synchronized
字节码指令
monitorenter指令(上锁)
源码位置:InterpreterRuntime::monitorenter
加锁入口
源码位置:ObjectSynchronizer::enter
fast锁实现
源码位置:ObjectSynchronizer::enter_fast_impl
膨胀为重锁
源码位置:ObjectSynchronizer::inflate_impl
里面有一个无限循环,多线程竞争cas操作,直到有线程成功膨胀为重锁
重锁
源码位置:ObjectMonitor::enter
重锁无法退回到fast锁,所以升级后每次加锁还是会尝试cas直接修改锁拥有人
源码位置:ObjectMonitor::TrySpin
源码位置:ObjectMonitor::enter
进入安全点后即可处理gc等操作
源码位置:ObjectMonitor::EnterI
因为外层操作上循环:每次循环后都会重新尝试同过自旋获取锁
重锁-阻塞获取锁前的最后挣扎
源码位置:ObjectMonitor::EnterI
到这里可以发现 cas是低成本&高效的获取锁方案(避免的cpu切换上下文)
源码位置:src/hotspot/share/runtime/objectMonitor.cpp:834
真正进入阻塞获取后,不再自旋而是定时或等待唤醒后尝试cas获取锁
monitorexit 指令(下锁)
源码位置:ObjectMonitor::ExitEpilog
monitor流程图
八股文?爬!
八股文中介绍的轻量锁、偏向锁、重量锁真是害人不浅的无意义内卷;