导读:本期聚焦于小伙伴创作的《什么是JVM的偏向锁、轻量级锁和重量级锁?synchronized锁升级过程是怎样的》,敬请观看详情,探索知识的价值。以下视频、文章将为您系统阐述其核心内容与价值。如果您觉得《什么是JVM的偏向锁、轻量级锁和重量级锁?synchronized锁升级过程是怎样的》有用,将其分享出去将是对创作者最好的鼓励。

在Java虚拟机中,synchronized关键字的实现依赖对象头中的Mark Word结构,JVM为了降低锁操作的开销,设计了偏向锁、轻量级锁、重量级锁三种状态,并且会根据竞争情况自动完成锁升级,这也是synchronized性能优化的重要体现。

什么是JVM的偏向锁、轻量级锁和重量级锁?synchronized锁升级过程是怎样的

三种锁的基本概念

偏向锁

偏向锁是JVM锁升级的第一阶段,它的核心思想是:如果一个线程获得了锁,那么锁就进入偏向模式,后续该线程再次获取锁时不需要进行任何同步操作,连CAS操作都不需要执行,直接判断线程ID是否匹配即可。

偏向锁适合只有一个线程访问同步块的场景,默认是开启的,可以通过JVM参数-XX:-UseBiasedLocking关闭偏向锁,关闭后程序默认直接进入轻量级锁状态。

轻量级锁

当偏向锁被另一个线程尝试获取时,偏向锁就会升级为轻量级锁。轻量级锁的核心思想是:在多线程交替执行同步块的场景下,避免使用操作系统层面的互斥量,而是通过CAS操作尝试获取锁。

轻量级锁的获取过程会在线程栈中创建锁记录,将对象头的Mark Word复制到锁记录中,然后尝试用CAS将对象头的Mark Word替换为指向锁记录的指针,如果成功则获取锁,失败则说明存在竞争,会膨胀为重量级锁。

重量级锁

重量级锁是锁升级的最终状态,依赖操作系统的互斥量(Mutex)实现,线程获取不到锁时会进入阻塞状态,等待操作系统调度唤醒。重量级锁的开销最大,因为涉及用户态和内核态的切换。

当轻量级锁CAS竞争失败,或者自旋次数超过阈值时,轻量级锁就会膨胀为重量级锁,此时对象的Mark Word会指向重量级锁的指针,后续所有竞争锁的线程都会直接阻塞。

synchronized锁升级的完整过程

synchronized的锁升级是单向的,只能从低级别向高级别升级,不能降级,完整流程如下:

  1. 初始状态下,对象头中的Mark Word处于无锁状态。
  2. 当第一个线程访问同步块并获取锁时,JVM会将锁状态标记为偏向锁,将线程ID记录在Mark Word中。
  3. 如果后续该线程再次进入同步块,只需要判断Mark Word中的线程ID是否和当前线程一致,一致则直接进入,不需要任何同步操作。
  4. 如果有另一个线程尝试获取这个锁,偏向锁就会被撤销,升级为轻量级锁。
  5. 轻量级锁状态下,线程通过CAS操作尝试获取锁,成功则执行同步块,失败则自旋等待。
  6. 如果自旋次数超过JVM设定的阈值(默认是10次,或者自适应自旋),或者存在第三个线程竞争锁,轻量级锁就会膨胀为重量级锁。
  7. 重量级锁状态下,获取不到锁的线程会进入阻塞队列,等待锁释放后被唤醒重新竞争。

锁状态对应的Mark Word结构

不同锁状态下,对象头Mark Word的结构不同,我们可以通过下表直观了解:

锁状态25bit4bit1bit(是否是偏向锁)2bit(锁标志位)
无锁对象的hashCode对象分代年龄001
偏向锁线程ID对象分代年龄101
轻量级锁指向栈中锁记录的指针000
重量级锁指向重量级锁的指针010

代码示例验证锁升级

我们可以通过简单的代码观察synchronized的锁升级过程,以下代码模拟了多线程竞争的场景:

public class SynchronizedUpgradeTest {
    public static void main(String[] args) throws InterruptedException {
        // 创建一个普通对象
        Object lock = new Object();
        
        // 第一个线程获取偏向锁
        new Thread(() -> {
            synchronized (lock) {
                System.out.println("线程1获取锁,当前应该是偏向锁状态");
                try {
                    Thread.sleep(2000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }).start();
        
        // 等待第一个线程启动
        Thread.sleep(500);
        
        // 第二个线程竞争锁,触发偏向锁升级为轻量级锁
        new Thread(() -> {
            synchronized (lock) {
                System.out.println("线程2获取锁,此时偏向锁会升级为轻量级锁");
            }
        }).start();
        
        // 等待前两个线程执行完成
        Thread.sleep(3000);
        
        // 多个线程同时竞争,触发轻量级锁升级为重量级锁
        for (int i = 0; i < 5; i++) {
            new Thread(() -> {
                synchronized (lock) {
                    System.out.println(Thread.currentThread().getName() + "获取锁,此时会升级为重量级锁");
                    try {
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }).start();
        }
    }
}

运行上述代码时,我们可以通过JVM工具观察锁状态的变化,当多个线程同时竞争锁时,最终锁会升级为重量级锁,此时线程会进入阻塞状态,性能开销会明显增大。

实际开发中的优化建议

  • 如果同步块只会单线程访问,不需要额外处理,JVM会默认使用偏向锁优化。
  • 如果同步块是多线程交替执行,没有激烈竞争,偏向锁和轻量级锁已经足够,不需要额外优化。
  • 如果存在激烈的多线程竞争,synchronized会自动升级为重量级锁,此时可以考虑使用并发包下的Lock接口实现类,比如ReentrantLock,根据场景选择公平锁或非公平锁。
  • 尽量不要在循环中使用synchronized,避免频繁获取释放锁带来的性能损耗。

synchronized偏向锁轻量级锁重量级锁JVM锁升级修改时间:2026-06-28 18:00:33

免责声明:​ 已尽一切努力确保本网站所含信息的准确性。网站内容多为原创整理与精心编撰,观点力求客观中立。本站旨在免费分享,内容仅供个人学习、研究或参考使用。若引用了第三方作品,版权归原作者所有。如内容涉及您的权益,请联系我们处理。
内容垂直聚焦
专注技术核心技术栏目,确保每篇文章深度聚焦于实用技能。从代码技巧到架构设计,为用户提供无干扰的纯技术知识沉淀,精准满足专业提升需求。
知识结构清晰
覆盖从开发到部署的全链路。AI、前端、编程、数据库、服务器、建站、系统层层递进,构建清晰学习路径,帮助用户系统化掌握开发与运维所需的核心技术。
深度技术解析
拒绝泛泛而谈,深入技术细节与实践难点。无论是数据库优化还是服务器配置,均结合真实场景与代码示例进行剖析,致力于提供可直接应用于工作的解决方案。
专业领域覆盖
精准对应开发生命周期。从前端界面到后端编程,从数据库操作到服务器运维,形成完整闭环,一站式满足全栈工程师和运维人员的技术需求。
即学即用高效
内容强调实操性,步骤清晰、代码完整。用户可根据教程直接复现和应用于自身项目,显著缩短从学习到实践的距离,快速解决开发中的具体问题。
持续更新保障
专注既定技术方向进行长期、稳定的内容输出。确保各栏目技术文章持续更新迭代,紧跟主流技术发展趋势,为用户提供经久不衰的学习价值。