一文带你认识Java中的锁升级机制

 更新时间:2025年12月21日 15:35:51   作者:笃行客从不躺平  
在Java中锁升级是一种重要的性能优化机制,用于提高多线程环境下并发访问的效率,这篇文章主要介绍了Java中锁级机制的相关资料,文中通过代码介绍的非常详细,需要的朋友可以参考下

一、锁升级的本质:JVM 的“自适应并发优化”

核心思想
大多数锁在运行时没有竞争,或竞争时间极短。与其一开始就使用重量级锁,不如先尝试更轻量的方式。

锁状态演进路径(HotSpot 64位 JVM)

无锁 → 偏向锁(Biased Locking) → 轻量级锁(Lightweight Locking) → 重量级锁(Heavyweight Locking)

⚠️ 关键特性:

  • 单向升级:锁只能升级,不能降级(除 GC 或显式撤销)
  • 基于对象头 Mark Word 状态切换
  • 由 JVM 自动决策,开发者无需干预

二、深入原理 + 场景 + 代码示例

1. 偏向锁(Biased Locking)—— 单线程的极致优化

底层原理

  • 对象头 Mark Word 中存储 偏向线程 ID
  • 同一线程再次进入同步块时,仅比对线程 ID,无需 CAS
  • 若对象未计算过 hashCode,则用 hashcode 字段存储线程 ID

Mark Word 结构(偏向锁状态)

| unused:25 | thread_id:54 | epoch:2 | age:4 | biased_lock:1 | lock:01 |

典型场景

  • Spring 单例 Bean 中的 synchronized 方法被同一线程反复调用
  • 批处理任务(如定时对账、数据清洗)

代码示例 & 验证

public class SingleThreadDemo {
    private final Object lock = new Object();

    public void processBatch(List<Data> dataList) {
        // 同一线程循环进入,触发偏向锁
        for (Data data : dataList) {
            synchronized (lock) {
                // 业务逻辑
                handle(data);
            }
        }
    }

    // 注意:若在此处调用 lock.hashCode(),会禁用偏向锁!
}

性能优势:接近无锁,仅首次 CAS 设置偏向,后续零开销。
致命缺陷:偏向锁撤销需 Stop-The-World(STW),影响延迟敏感系统。

2. 轻量级锁(Lightweight Locking)—— 低竞争的优雅退让

底层原理

  • 当第二个线程尝试获取已被偏向的锁时,撤销偏向锁
  • 线程在栈帧中创建 Lock Record
  • 通过 CAS 将对象头 Mark Word 替换为指向 Lock Record 的指针
  • 若 CAS 失败,线程自旋重试(默认 10 次),失败则膨胀为重量级锁

典型场景

  • Web 请求处理(低并发、请求串行化)
  • 日志写入(多线程交替写,但不同时)

代码示例

@RestController
public class OrderController {
    private final Object orderLock = new Object();
    
    @PostMapping("/create")
    public ResponseEntity<?> createOrder(@RequestBody OrderRequest req) {
        synchronized (orderLock) { // 多个请求线程交替进入
            validate(req);
            return orderService.create(req);
        }
    }
}

优势:避免线程阻塞/唤醒的系统调用开销(上下文切换成本约 1~10μs)
风险:自旋浪费 CPU,在 CPU 密集型场景可能恶化性能

3. 重量级锁(Heavyweight Locking)—— 高竞争的最终保障

底层原理

  • 基于 OS Mutex(互斥量) 实现
  • 竞争线程被挂起(park()),放入 EntryList 队列
  • 持有锁线程释放时,唤醒队列中的一个线程(unpark()
  • 涉及 用户态 ↔ 内核态切换,开销大(约 1~10μs)

典型场景

  • 秒杀库存扣减
  • 全局配置更新
  • 数据库连接池管理

反面教材 vs 正确做法

// ❌ 反面:synchronized 导致重量级锁竞争
public class InventoryService {
    private int stock = 100;
    public synchronized boolean deduct() {
        if (stock > 0) {
            stock--; // 高并发下性能极差
            return true;
        }
        return false;
    }
}

// ✅ 正确:使用 LongAdder(分段无锁)
public class OptimizedInventory {
    private final LongAdder deducted = new LongAdder();
    private final int totalStock = 100;
    
    public boolean deduct() {
        long current = deducted.sum();
        if (current >= totalStock) return false;
        deducted.increment(); // 无锁,分段累加
        return true;
    }
}

三、生产环境实战:如何应用与调优?

生产最佳实践

场景推荐方案原理
微服务高并发关闭偏向锁避免频繁撤销导致 STW
计数器/状态LongAdder / AtomicXXX分段无锁,避免全局竞争
读多写少StampedLock乐观读,提升吞吐
复杂同步逻辑ReentrantLock + Condition支持超时、中断、公平锁

JVM 参数调优(生产常用)

# 关闭偏向锁(推荐微服务场景)
-XX:-UseBiasedLocking

# 禁用偏向锁延迟(启动即生效)
-XX:BiasedLockingStartupDelay=0

# 监控锁统计(诊断用)
-XX:+PrintBiasedLockingStatistics
-XX:+UnlockDiagnosticVMOptions -XX:+PrintSafepointStatistics

# 调整自旋次数(谨慎!)
-XX:PreBlockSpin=15

📌 真实案例
某电商平台在大促前发现 GC Pause 异常升高,经 JFR 分析发现 偏向锁撤销频繁触发 safepoint。关闭 -XX:-UseBiasedLocking 后,P99 延迟下降 40%。

四、优缺点深度分析

锁类型优点缺点适用边界
偏向锁单线程零开销撤销需 STW,不适合多线程交替单线程长期持有(< 1% 生产场景)
轻量级锁避免内核切换自旋浪费 CPU,仅适合短临界区低竞争 + 临界区 < 10μs
重量级锁强一致性保障上下文切换开销大高竞争或长临界区

💡 关键洞察
锁升级是 JVM 的“事后补救”机制。真正的高性能系统应从架构层面避免锁竞争,而非依赖锁优化。

五、实际开发选型策略 —— 高级工程师思维

决策树(体现深度)

高级建议(面试加分项)

  1. 优先无锁:使用 volatile + CAS(如 AtomicReference
  2. 缩小锁粒度:只锁临界区,而非整个方法
    // Bad
    public synchronized void method() { /* ... */ }
    
    // Good
    public void method() {
        // 非临界区
        prepare();
        synchronized (this) {
            // 临界区
        }
    }
  3. 监控锁竞争:使用 Arthas、JFR 分析
    # Arthas 查看 BLOCKED 线程
    thread --state BLOCKED
    
    # JFR 记录锁事件
    java -XX:StartFlightRecording=duration=60s,filename=lock.jfr ...

六、面试深度

Q1: 为什么现代微服务要关闭偏向锁?

:微服务使用线程池复用线程,多个请求线程交替访问同一对象,导致偏向锁频繁撤销。而撤销需触发 safepoint(STW),在高 QPS 下会显著增加 P99 延迟。Kafka、Netty 等高性能框架均默认关闭。

Q2: synchronized 和 ReentrantLock 在 JDK 17 下性能对比?

:JDK 6+ 对 synchronized 进行了锁消除、锁粗化、锁升级等优化,在低竞争场景性能优于 ReentrantLock(后者始终走重量级路径)。但 ReentrantLock 提供更多功能(超时、公平锁、Condition),适用于复杂同步场景。

Q3: 如何证明锁升级发生了?

:可通过以下方式验证:

  1. 使用 JOL(Java Object Layout) 打印对象头
  2. 开启 JVM 参数 -XX:+PrintBiasedLockingStatistics
  3. 使用 JFR(Java Flight Recorder) 记录 Monitor Blocked 事件

总结:高级工程师的并发观

锁升级是 JVM 的“安全网”,但真正的高手从不依赖安全网。应该:

  • 理解原理:知道 JVM 如何优化,也知其局限
  • 规避竞争:通过无状态设计、分片、异步化减少锁需求
  • 工具驱动:用 JFR、Arthas 验证假设,而非猜测
  • 权衡取舍:在一致性、性能、复杂度间找到平衡点

“最好的并发控制,是没有并发控制。”
—— 通过架构设计从根本上消灭锁,才是高级工程师的终极追求。

到此这篇关于Java中锁升级机制的文章就介绍到这了,更多相关Java锁升级机制内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • Java UrlRewriter伪静态技术运用深入分析

    Java UrlRewriter伪静态技术运用深入分析

    通常我们为了更好的缓解服务器压力,和增强搜索引擎的友好面,都将文章内容生成静态页面,这就产生了伪静态技术,也就是我们常说的Url Rewriter重写技术
    2012-12-12
  • SpringBoot集成ShedLock实现分布式定时任务

    SpringBoot集成ShedLock实现分布式定时任务

    ShedLock 是一个 Java 库,通常用于分布式系统中,确保定时任务(Scheduled Tasks)在集群环境下只被某一个实例执行一次,它通过在共享资源中添加锁的方式,本文给大家介绍了SpringBoot集成ShedLock实现分布式定时任务,需要的朋友可以参考下
    2024-11-11
  • java实现对服务器的自动巡检邮件通知

    java实现对服务器的自动巡检邮件通知

    这篇文章主要为大家详细介绍了java实现对服务器的自动巡检邮件通知,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2017-05-05
  • 关于maven pom文件配置加载lib下的jar包

    关于maven pom文件配置加载lib下的jar包

    在项目中引用放置在lib文件夹中的第三方jar包需要通过POM文件进行特定配置,虽然将依赖放置在公司的Maven私服是更好的做法,但如果遇到部署问题,可以利用maven-jar-plugin进行配置,Spring Boot项目可以通过特定设置来实现
    2024-09-09
  • Java 设计模式以虹猫蓝兔的故事讲解建造者模式

    Java 设计模式以虹猫蓝兔的故事讲解建造者模式

    建造者模式,是一种对象构建模式 它可以将复杂对象的建造过程抽象出来,使这个抽象过程的不同实现方法可以构造出不同表现的对象。本文将通过示例讲解建造者模式,需要的可以参考一下
    2022-04-04
  • springBoot详解集成Swagger流程

    springBoot详解集成Swagger流程

    Swagger是一个规范和完整的框架,用于生成、描述、调用和可视化 Restful 风格的 Web 服务。总体目标是使客户端和文件系统作为服务器以同样的速度来更新。文件的方法、参数和模型紧密集成到服务器端的代码,允许API来始终保持同步
    2022-06-06
  • SpringBoot+Nacos+Kafka微服务流编排的简单实现

    SpringBoot+Nacos+Kafka微服务流编排的简单实现

    本文主要介绍了SpringBoot+Nacos+Kafka微服务流编排的简单实现,文中通过示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2021-08-08
  • Java实现较大二进制文件的读、写方法

    Java实现较大二进制文件的读、写方法

    本篇文章主要介绍了Java实现较大二进制文件的读、写方法,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2017-02-02
  • 基于module-info.class的问题

    基于module-info.class的问题

    这篇文章主要介绍了基于module-info.class的问题,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2022-01-01
  • SpringBoot统一处理功能实现的全过程

    SpringBoot统一处理功能实现的全过程

    最近在做项目时需要对异常进行全局统一处理,主要是一些分类入库以及记录日志等,下面这篇文章主要给大家介绍了关于SpringBoot统一功能处理实现的相关资料,文中通过图文以及实例代码介绍的非常详细,需要的朋友可以参考下
    2023-03-03

最新评论