内存布局对应对应的锁状态

先说锁状态的变化结论

偏向锁
- 偏向锁是一种针对加锁操作的优化手段。
- 在大多数情况下,锁不仅不存在多线程竞争,而且总是由同一线程多次获得,因此为了消除数据在无竞争情况下锁重入(CAS操作)的开销而引入偏向锁。
- 对于没有锁竞争的场合,偏向锁有很好的优化效果。
- JVM启用了偏向锁模式:jdk6之后默认开启
- 新创建对象的Mark Word中的Thread Id为0,说明此时处于可偏向但未偏向任何线程,也叫做匿名偏向状态(anonymously biased)。
偏向锁延迟偏向
- HotSpot 虚拟机在启动后开启偏向锁模式默认在4s后。
- 为了减少初始化时间,JVM默认延时加载偏向锁。
-XX:BiasedLockingStartupDelay=0
-XX:-UseBiasedLocking

偏向锁在无竞争的时候一直是偏向锁
public static void main(String[] args) throws InterruptedException {
log.debug(Thread.currentThread().getName() + "最开始的状态。。。\n"
+ ClassLayout.parseInstance(new Object()).toPrintable());
Thread.sleep(4000);
Object obj = new Object();
new Thread(new Runnable() {
@Override
public void run() {
log.debug(
Thread.currentThread().getName() + "开始执行准备获取锁。。。\n" + ClassLayout.parseInstance(obj).toPrintable());
synchronized (obj) {
log.debug(Thread.currentThread().getName() + "获取锁执行中。。。\n"
+ ClassLayout.parseInstance(obj).toPrintable());
}
log.debug(Thread.currentThread().getName() + "释放锁。。。\n" + ClassLayout.parseInstance(obj).toPrintable());
}
}, "thread1").start();
Thread.sleep(5000);
log.debug(Thread.currentThread().getName() + "结束状态。。。\n" + ClassLayout.parseInstance(obj).toPrintable());
}

- 从结果可以看出:无锁状态经过4秒变为偏向锁,之后的的状态一直是偏向锁!
- 在进入同步代码块后,锁的偏向线程由0变为具体的线程。
在同步代码块外调用hashCode()方法

- 进入同步代码块后锁升级为轻量级锁
- 当对象可偏向(线程ID为0)时,MarkWord将变成未锁定状态,并只能升级成轻量锁。
在同步代码块内调用hashCode()方法

- 直接升级为重量级锁
- 当对象正处于偏向锁时,调用HashCode将使偏向锁强制升级成重量锁。
偏向锁撤销:自己验证wait和notify
- 调用锁对象的obj.hashCode()或System.identityHashCode(obj)方法会导致该对象的偏向锁被撤销。
- 因为对于一个对象,其HashCode只会生成一次并保存,偏向锁是没有地方保存hashcode的。
- 轻量级锁会在锁记录中记录 hashCode。
- 重量级锁会在 Monitor 中记录 hashCode。
- 当对象可偏向(线程ID为0)时,MarkWord将变成未锁定状态,并只能升级成轻量锁。
- 当对象正处于偏向锁时,调用HashCode将使偏向锁强制升级成重量锁。
- 偏向锁状态执行obj.notify() 会升级为轻量级锁。
- 调用obj.wait(timeout) 会升级为重量级锁。
轻量级锁
- 倘若偏向锁失败,虚拟机并不会立即升级为重量级锁,它还会尝试使用一种称为轻量级锁的优化手段,此时Mark Word 的结构也变为轻量级锁的结构。
- 轻量级锁所适应的场景是线程交替执行同步块的场合,如果存在同一时间多个线程访问同一把锁的场合,就会导致轻量级锁膨胀为重量级锁。
- 轻量级锁在降级的时候直接变为无锁状态!(查看之前在同步代码块外调用hashCode()方法)
模拟竞争不激烈的场景
@Slf4j
public class TestMemory {
public static void main(String[] args) throws InterruptedException {
log.debug(Thread.currentThread().getName() + "最开始的状态。。。\n"
+ ClassLayout.parseInstance(new Object()).toPrintable());
Thread.sleep(4000);
Object obj = new Object();
Thread thread1 = new Thread(new Runnable() {
@Override
public void run() {
log.debug(Thread.currentThread().getName() + "开始执行thread1。。。\n"
+ ClassLayout.parseInstance(obj).toPrintable());
synchronized (obj) {
log.debug(Thread.currentThread().getName() + "获取锁执行中thread1。。。\n"
+ ClassLayout.parseInstance(obj).toPrintable());
}
log.debug(Thread.currentThread().getName() + "释放锁thread1。。。\n"
+ ClassLayout.parseInstance(obj).toPrintable());
}
}, "thread1");
thread1.start();
Thread.sleep(1);
Thread thread2 = new Thread(new Runnable() {
@Override
public void run() {
log.debug(Thread.currentThread().getName() + "开始执行thread2。。。\n"
+ ClassLayout.parseInstance(obj).toPrintable());
synchronized (obj) {
log.debug(Thread.currentThread().getName() + "获取锁执行中thread2。。。\n"
+ ClassLayout.parseInstance(obj).toPrintable());
}
log.debug(Thread.currentThread().getName() + "释放锁thread2。。。\n"
+ ClassLayout.parseInstance(obj).toPrintable());
}
}, "thread2");
thread2.start();
Thread.sleep(5000);
log.debug(Thread.currentThread().getName() + "结束状态。。。\n" + ClassLayout.parseInstance(obj).toPrintable());
}
}
竞争不激烈的场景的运行结果

重量级锁
- 轻量级锁经过一次自选如果没有获取到锁,直接膨胀为重量级锁。
- 重量级锁是基于 Monitor 机制,并且在 Monitor 中记录 hashCode
模拟竞争激烈的场景
Thread.sleep(1);
竞争激烈的场景的运行结果

结束语
- 获取更多有价值的文章,让我们一起成为架构师!
- 关注公众号,可以让你对MySQL有非常深入的了解
- 关注公众号,每天持续高效的了解并发编程!
- 关注公众号,后续持续高效的了解spring源码!
- 这个公众号,无广告!!!每日更新!!!

【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· AI技术革命,工作效率10个最佳AI工具