Loading [MathJax]/jax/output/CommonHTML/config.js
前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >【高薪程序员必看】万字长文拆解Java并发编程!(4-2):悲观锁底层原理与性能优化实战

【高薪程序员必看】万字长文拆解Java并发编程!(4-2):悲观锁底层原理与性能优化实战

作者头像
摘星.
发布于 2025-05-20 06:25:59
发布于 2025-05-20 06:25:59
3900
代码可运行
举报
文章被收录于专栏:博客专享博客专享
运行总次数:0
代码可运行
Hello大家好!👋 我是摘星✨,今天我们来深度拆解Java并发编程中最经典的​「悲观锁」🔒设计。

在多线程环境下,当你的转账操作被重复提交💸、库存被超卖📉、计数器结果离奇错误❌时,背后往往是因为缺乏合理的锁控制。而悲观锁作为Java并发中最「简单粗暴」的解决方案,从JDK1.0时代的重量级锁⛓️,到如今JVM层级的锁升级优化⚡,其底层实现堪称一部高性能并发的发展史📜

本文将带你穿透**synchronized关键字**的表面语法,直击三大核心问题💡:

  1. ​**🤔 为什么悲观锁能保证线程安全?**​(从Java对象头到Monitor的硬件级协作)
  2. ​**⚡ JDK1.6后synchronized如何实现性能飞跃?**​(偏向锁/轻量级锁的取舍智慧)
  3. ​**🚫 高并发场景下如何规避锁的性能陷阱?**​(从字节码层面理解锁膨胀的条件)

📌 举个真实案例:某电商平台在秒杀活动中使用synchronized导致TPS从8000📈暴跌到300📉,最终通过缩小锁粒度+锁分离优化提升15倍性能🚀——我们将在文中用代码还原这个优化过程。

下面我们直接切入正题,从操作系统与JVM的协作契约🤝开始讲起!

4.5. 锁记录

锁记录(Lock Record)是jvm用来支持轻量级锁的数据结构,是线程私有的,用于记录锁的状态,锁拥有者的信息以及竞争情况等等.

  1. 指向拥有者的指针(Owner Pointer):记录当前拥有锁的线程ID。在加锁的过程中,将对象头部的指针指向锁记录,以便快速定位拥有锁的线程。
  2. 标记位(Displacement):记录了对象头部的偏移量,用于在解锁的过程中恢复对象头部的标记字段。
  3. 锁状态(Lock State):记录了当前线程锁的状态,包括偏向锁、轻量级锁、重量级锁等。

在竞争情况下,多个线程会通过锁记录进行协调,实现了对轻量级锁的竞争和重量化的升级。当多个线程竞争同一把锁时,会通过锁记录中的信息协调竞争关系,避免无谓的自旋和竞争。

4.6. 偏向锁

偏向锁:当没有没有锁竞争时可以用偏向锁来优化

偏向锁的加锁过程:

  • 偏向锁加锁:当线程第一次访问的同步代码块时,jvm会将该对象的锁标记为偏向锁01,同时将对象头中的mark word指向线程ID
  • 偏向锁检查:当线程再次访问同一个加锁对象的synchronized(obj)代码块时,会先检查该对象的锁标记是否为偏向锁,是偏向锁且线程ID与当前线程ID一致,则无需加锁,直接进入代码块

偏向锁撤销过程:

  • 当其他线程访问这个同步代码块时,发现线程ID不一致,偏向锁就会被撤销,升级为轻量级锁
  • 调用wait/notify方法是也会撤销偏向锁,因为wait/notify方法只有重量锁才有

4.7. 轻量级锁

轻量级锁:如果有多个线程使用synchronized对一个对象加锁,但是加锁的时间是错开的,不会发生竞争时,可以使用轻量级锁来优化.

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
static final Object obj = new Object();
public static void method1(){
    synchronized(obj){
        method2()
    }
}
public static void method2(){
    synchronized(obj){

    }
}

轻量级锁的加锁过程:

  1. 检测偏向锁:线程进入同步代码块时,首先会尝试偏向锁,如果对象的对象头中的锁标识位是01(偏向锁),且当前线程是偏向锁的拥有者,则直接进入同步代码块执行,不需要加轻量级锁.如果对象没有偏向锁,则会进入下一步CAS操作尝试获取轻量级锁.
  2. CAS操作尝试获取轻量级锁:线程进入同步代码块时会尝试通过CAS操作将对象头的markword指向锁记录
    • CAS成功:当前线程成功获取了轻量级锁,对象锁标识位更新为00(轻量级锁)
    • CAS失败-竞争:说明有其他线程也在竞争该对象锁,那么当前线程会进行自旋,尝试在短时间内快速获取锁
    • CAS失败-重入:说明当前线程执行了一次加锁过程,会添加一条锁记录

轻量级锁的解锁过程:

  1. CAS操作尝试释放轻量级锁:当持有轻量级锁的线程退出同步块时,会尝试通过CAS操作将对象头中的markword恢复
    • CAS成功:当前线程成功释放了轻量级锁m对象锁标识位更新为01(无锁状态),同时重置锁记录
    • CAS失败:说明有其他线程正在竞争锁,此时轻量级锁会膨胀为重量级锁

4.8. 锁膨胀

轻量级锁竞争:当前线程加轻量级锁的CAS操作失败时,说明发生了锁的竞争,这时当前线程会进行自旋获取锁.自旋是通过忙等待,在一个循环中不断获取锁

自旋锁膨胀为重量级锁:当自旋达到一定的阈值时,会导致锁膨胀,将轻量级锁膨胀为重量级锁

  1. 当前线程为加锁对象申请Monitor锁,将对象头中的markword指向Monitor地址,锁标识位改为10
  2. 当前线程进入EntryList中,处于阻塞状态
  3. 当持锁的线程退出同步代码块时,尝试用CAS操作将对象头中的markword恢复
    • CAS成功:成功释放锁
    • CAS失败:进入重量级锁的解锁过程,将Monitor的owner清空,并将EntryList中阻塞的线程唤醒来竞争锁

4.9. ReentrantLock

4.9.1. ReentrantLock与synchronized的对比

在Java多线程中,ReentrantLock是一个可重入锁,相比与synchronized有更多的灵活性

共同点:

  • 可重入:ReentrantLock和synchronized都是可重入锁,即对一个对象反复加锁

不同点:

  • 锁等待可终止:ReentrantLock允许一个线程在等待锁的过程中被其他线程打断,然后可以选择继续等待获取终止.synchronized获取锁的过程中是不可被打断的
  • 超时时间:ReentrantLock允许设置获取锁的超时时间,超过时间之后可以放弃获取这个锁.synchronized不能设置超时时间
  • 公平锁选择:ReentrantLock允许选择公平锁或者非公平锁.synchronized只有非公平锁
    • 在公平锁模式下,等待时间最长的线程将优先获取锁的访问权限,按先入先得的方式,防止饥饿现象出现,但是会降低并发度
    • 在非公平锁模式下,让等待中的线程竞争锁,具有随机性
  • 多个条件变量:ReentrantLock提供了Condition允许多个条件变量存在,也就是多个waitlist,可以让线程满足某个条件时等待,也可以让其他线程满足某个条件时唤醒.synchronized只有一个条件变量
  • synchronized是关键字级别,ReentrantLock是对象级别
4.9.2. API介绍

private Lock lock = new ReentrantLock()

  • lock.lock():获取锁,不可被打打断
  • lock.unlock():释放锁
  • lock.lockInterruptibly():可打断,获取锁:获取锁失败进入阻塞状态时,可以被其他线程的interrput方法打断
  • lock.trylock():尝试获得锁,返回值是boolean,true获取锁成功,false获取锁失败
  • lock.trylock(long n,TimeUnit.SECONDS):尝试获得锁,获取不到锁等待n单位时间,时间到了还没获取到锁就返回false,时间内获取到锁还是返回true,支持可打断
  • new ReentrantLock(true):设置公平锁,默认为false

Condition condition = lock.newCondition()//Condition可以创建多个也就是支持多个条变量

  • condition.await():await使用前需要获取锁,调用后释放锁,进入condition条件变量中等待,被唤醒(打断)后继续竞争lock锁,竞争成功后继续执行
  • condition.await(long timeout,TimeUnit unit):等待指定超时时间
  • condition.signal():从condition中随机唤醒一个等待中的线程
  • condition.signalAll():从condition中唤醒全部等待中的线程
  • condition.awaitUninterruptibly():等待不可被打断
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
try {
    // 获取锁 : 不可被打断
    lock.lock(); 
    // 在这里执行需要同步的代码块
} finally {
    // 释放锁
    reentrantLock.unlock(); 
}
🎯 悲观锁总结:从原理到实战的完整指南 🔒➡️⚡
🔍 核心要点回顾
  1. 悲观锁哲学 🛡️
  • "宁可错杀,不可放过"的设计理念
  • 适合写多读少场景(支付/库存系统💰)
  1. synchronized进化史 🧬
  • JDK1.6前:重量级锁⛓️(微秒级延迟)
  • JDK1.6后:锁升级路径(无锁→偏向锁→轻量级锁→重量级锁)
  1. 关键对比 ⚖️

特性

synchronized

ReentrantLock

条件变量

单个

多个

可中断

超时控制

公平锁

💡​场景选择指南 🗺️
  • 简单场景 → synchronized(自动管理)
  • 复杂需求 → ReentrantLock(超时/公平锁)
  1. 性能监控工具 🔧
  • 使用JOL查看对象头:System.out.println(ClassLayout.parseInstance(obj).toPrintable())
  • Arthas监控锁竞争:monitor -c 5 com.example.MyClass methodName
🚀 进阶路线
  1. 下篇预告 📢
  • 无锁编程:CAS原理揭秘
  • Atomic类性能对比
  1. 思考挑战 💭 当QPS超过10万时:
  • 偏向锁是否反而降低性能?
  • 如何选择自旋次数阈值?
📚 扩展学习
  1. 推荐书籍
  • 《Java并发编程实战》📖
  • 《并发编程的艺术》🎨
  1. 实战项目
  • 实现一个带性能统计的锁装饰器
  • 对比不同锁在秒杀场景的表现
🌟 一句话总结

"悲观锁用性能换取安全,而真正的艺术在于找到那个平衡点⚖️"

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2025-04-11,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
暂无评论
推荐阅读
编辑精选文章
换一批
【高薪程序员必看】万字长文拆解Java并发编程!(4-1):悲观锁底层原理与性能优化实战
在多线程环境下,当你的转账操作被重复提交💸、库存被超卖📉、计数器结果离奇错误❌时,背后往往是因为缺乏合理的锁控制。而悲观锁作为Java并发中最「简单粗暴」的解决方案,从JDK1.0时代的重量级锁⛓️,到如今JVM层级的锁升级优化⚡,其底层实现堪称一部高性能并发的发展史📜。
摘星.
2025/05/20
770
【高薪程序员必看】万字长文拆解Java并发编程!(4-1):悲观锁底层原理与性能优化实战
Java并发编程之synchronized底层原理
对象头包含两部分:运行时元数据(Mark Word)和类型指针 (Klass Word)
Java微观世界
2025/01/21
1360
Java并发编程之synchronized底层原理
并发编程锁之synchronized(二)
并发编程中数据同步需要依赖锁进行控制,上篇博文通过ReentrantLock源码分析也对Lock实现锁机制的大致原理有了一个了解,Lock主要是通过编码的方式实现锁,其核心就是:CAS+循环,CAS原子操作需要依赖底层硬件层特殊的CPU指令。这节我们来看下Java中另一种非常常见的实现同步的方式:synchronized。synchronized主要通过底层JVM进行实现,而且JVM为了优化,产生偏向锁、轻量级锁、重量级锁,由于其处于JVM底层实现中,对很多并发编程人员来说能清晰理解它们间的区别还是件困难的事。通过本篇博文,构建出对Java中锁得体系结构,让你对其有个更系统全面的认知。
技术zhai
2019/02/15
4490
快速掌握并发编程---锁优化篇
重量级锁就是如果存在线程竞争,会把线程 挂起来,等其他线程释放锁后,再去唤醒挂起的线程。因为线程的挂起和唤醒需要从内核态到用户态的切换,这个切换需要操作系统的支持,性能消耗非常大。
田维常
2020/10/23
3130
快速掌握并发编程---锁优化篇
万字长文带你了解Java中锁的分类
Java中的锁是一种多线程编程中的同步机制,用于控制线程对共享资源的访问,防止并发访问时的数据竞争和死锁问题。通过使用锁机制,可以实现数据的同步访问,确保多个线程安全地访问共享资源,从而提高程序的并发性能。
索码理
2023/08/21
5880
万字长文带你了解Java中锁的分类
《面试补习》- Java锁知识大梳理
悲观锁,总是假设最坏的情况,每次去拿数据的时候都认为别人会修改,所以每次在拿数据的时候都会上锁,这样别人想拿这个数据就会阻塞直到它拿到锁。
九灵
2020/06/28
6210
synchronized关键字 Krains 2020-08-25
synchronized实际是用对象锁保证了临界区内代码的原子性(临界区就是多个线程对共享资源读写操作的代码块),临界区内的代码对外是不可分割的,不会因线程切换所打断。
Krains
2020/09/10
3400
synchronized关键字  Krains 2020-08-25
Java里有哪些锁?Synchronized如何实现同步锁?
说到Java中的锁,大伙们到底知道多少呢?这可是面试中常问的话题哦。在说Java中有哪些锁之前,首先咱们说说Java锁是什么,他解决了什么问题?
攻城狮的那点事
2019/09/25
3.4K0
Java 锁分类
乐观锁是一种乐观思想,认为读多写少,遇到并发的可能性低,每次拿数据时候并不会上锁,因为认为不会被别人修改。但是更新的时候会判断有没有人会更新这条数据,采取写的时候先读取版本号然后加锁,主要是和上一次版本号进行比较,如果一样则更新这条数据,如果不一样则会重复读,比较,写操作。它是基于CAS来实现的。
Yif
2019/12/26
7930
Java并发编程:synchronized和锁优化
1. 使用方法 synchronized 是 java 中最常用的保证线程安全的方式,synchronized 的作用主要有三方面: 确保线程互斥的访问代码块,同一时刻只有一个方法可以进入到临界区 保证共享变量的修改能及时可见 有效解决重排序问题 语义上来讲,synchronized主要有三种用法: 修饰普通方法,锁的是当前对象实例(this) 修饰静态方法,锁的是当前 Class 对象(静态方法是属于类,而不是对象) 修饰代码块,锁的是括号里的对象 2. 实现原理 2.1. 监视器锁 synchroniz
butterfly100
2018/04/16
8930
Java并发编程:synchronized和锁优化
关于Java的那些“锁”事
Java中的分很多种类,按照场景的不同、特性的不同等分为了很多类,下面就来讲讲Java中锁的概念:
Qwe7
2022/02/16
4640
58面试官居然问我Synchronized底层实现,锁升级的具体过程?
这是我去年7,8月份面58被问的一个面试题,说实话被问到这个问题还是很意外的,感觉这个东西没啥用啊,直到后面被问了一波new Object,Integer对象等作为加锁对象行吗?会出现哪些问题?为啥java6后synchronized性能大幅上升?我彻底蒙蔽了。下面详细总结一下
Java识堂
2020/02/19
8011
java并发编程实战(2) 线程同步synchronized
synchronized保证语句块内操作是原子的,所谓原子性就是指一个操作或者多个操作,要么全部执行并且执行的过程不会被任何因素打断,要么就都不执行。被synchronized修饰的类或对象的所有操作都是原子的,因为在执行操作之前必须先获得类或对象的锁,直到执行完才能释放,这中间的过程无法被中断。synchronized和volatile最大的区别就在于原子性,volatile不具备原子性。
黄规速
2022/04/14
4990
java并发编程实战(2) 线程同步synchronized
Java锁概论
Java中的锁主要用于保障多并发线程情况下数据的一致性。在多线程编程中为保障数据一致性,我们常需要在使用对象或方法之前加锁。这时若有其他线程也需要使用此对象或該方法,则产生要获得锁。如果某个线程发现锁正被其他线程使用,就会进入阻塞队列等待锁的释放,直到其它线程执行完毕并释放锁,該线程才有机会再次获取锁进行操作。这就保障了在同一时刻只有一个线程持有该对象的锁并修改对象,从而保障数据安全。
Surriento
2024/08/05
850
JAVA并发编程系列(4)一文看懂全部锁机制
曾几何时,面试官问:java都有哪些锁?小白,一脸无辜:用过的有synchronized,其他不清楚。面试官:回去等通知!
拉丁解牛说技术
2024/09/09
2920
synchronized 与多线程的哪些关系
JVM 实现的 synchronized JDK 实现的 ReentrantLock
BUG弄潮儿
2022/06/30
2800
synchronized 与多线程的哪些关系
【并发编程】2 synchronized底层实现原理、Java内存模型JMM;monitor、CAS、乐观锁和悲观锁;对象的内存结构、Mark Word、锁升级
本文为5、6小节,1~4节请查阅【并发编程】1 synchronized底层实现原理、Java内存模型JMM;monitor、CAS、乐观锁和悲观锁;对象的内存结构、Mark Word、锁升级
寻求出路的程序媛
2024/05/13
1390
【并发编程】2 synchronized底层实现原理、Java内存模型JMM;monitor、CAS、乐观锁和悲观锁;对象的内存结构、Mark Word、锁升级
Java多线程与并发-原理
解决问题的根本方法: 同一时刻有且只有一个线程在操作共享数据,其他线程必须等到该线程处理完数据后再对共享数据进行操作。
ha_lydms
2023/08/10
4580
Java多线程与并发-原理
Java中的锁 Lock和synchronized
锁是java并发编程中最重要的同步机制。锁除了让临界区互斥执行外,还可以让释放锁的线程向获取同一个锁的线程发送消息。
Java技术江湖
2019/09/25
5110
synchronized 锁的原理
在多线程并发编程中 synchronized 一直是元老级角色,很多人都会称呼它为重量级锁。但是,随着 Java SE 1.6 对synchronized 进行了各种优化之后,有些情况下它就并不那么重,Java SE 1.6 中为了减少获得锁和释放锁带来的性
会说话的丶猫
2020/08/06
5160
推荐阅读
相关推荐
【高薪程序员必看】万字长文拆解Java并发编程!(4-1):悲观锁底层原理与性能优化实战
更多 >
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档