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

简介: 目录4. JVM字节码文件4.1. 字节码文件-组成4.1.1. 组成-基础信息4.1.1.1. 基础信息-魔数4.1.1.2. 基础信息-主副版本号4.1.2. 组成-常量池4.1.3. 组成-方法4.1.3.1. 方法-工作流程4.1.4. 组成-字段4.1.5. 组成-属性4.2. 字节码文件-查看工具4.2.1. javap4.2.2. jclasslib4.2.3. 阿里Arthas

 

image.gif 编辑

Hello大家好!👋 我是摘星✨,今天我们来深度拆解Java并发编程中最经典的「悲观锁」🔒设计。

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

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

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

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

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

目录

4. 有锁并发-悲观锁

4.1. 悲观锁思想

4.2. Synchronized

4.3. Java对象头

4.4. Monitor


4. 有锁并发-悲观锁

4.1. 悲观锁思想

悲观锁:假设其他线程会修改共享资源,在进入同步代码块时就加上锁,防止其他线程的干扰。但是如果竞争激烈,就会发生线程上下文切换,性能相对低。

4.2. Synchronized

synchronized关键字,基于Monitor实现,用于实现多线程之间的同步,是悲观锁

synchronized的执行流程:

  1. 第一个线程获取对象锁执行同步代码块
  2. 其他想要获取对象锁进入同步代码块的线程进入阻塞状态
  3. 第一个线程退出同步代码块释放锁后,会唤醒阻塞中的线程
  4. 被唤醒的线程进行非公平的锁争抢,抢到锁的线程执行同步代码块,其他线程继续阻塞

synchronized的特性:

  1. 原子性(Atomicity):synchronized保证同时只能有一个线程执行同步代码块,对共享数据的操作是原子的。
  2. 可见性(Visibility):一个线程执行synchronized同步代码块时,会先获取主内存中的数据到工作内存中,修改共享资源,退出同步代码块时,会将工作内存中的数据刷新到主内存中,保证数据的可见性。
  3. 有序性(Ordering):synchronized会禁止JVM对字节码指令重排优化,保证线程执行的有序性。

synchronized的作用范围:

  1. 成员方法:synchronized修饰成员方法时,锁住的是当前对象实例。
  2. 静态方法:synchronized修饰静态方法时,锁定的是当前类的Class对象。
  3. 代码块:synchronized修饰代码块时,锁定的是指定的对象。

多线程只有获取同一个对象的锁才能起到同步互斥的效果

JDK1.6之前synchronized是重量级锁,jdk1.6之后对synchronized做了一系列优化,加入了偏向锁和轻量级锁,优化了线程上下文切换的性能

4.3. Java对象头

synchronized是悲观锁,在操作共享资源之前需要先加锁,加的锁就存在于Java对象头中.

Hotspot虚拟机中的对象头主要包含两部分数据:Mark Word(标记字段)和Klass Pointer(类型指针)

  • Mark Word:存储对象的hashcode,分代年龄,锁标志位信息.这些信息跟对象自定定义无关,所以Mark Word被设计为非固定的数据结构以便在极小的空间中存储多的数据,它会根据对象的状态复用空间,也就是说在运行期间,Mark Word中的存储数据会跟着锁标志位变化而变化

    image.gif 编辑
  • Klass Pointer:指向类元数据的指针,用于确定当前对象是哪个类的实例

4.4. Monitor

Monitor是一种同步机制,依赖于操作系统底层的Mutex Lock(同步锁)来实现同步,在Java中,每一个Java对象都会关联一个Monitor对象,synchronized关键字就是通过Monitor来实现线程同步的,是重量级锁

Monitor的组成部分:

  • owner(所有者):用于存放当前拥有了Monitor对象的线程,也就是拥有对象锁的线程,只有owner线程才能执行同步代码块.
  • waitlist(等待队列):用于存放因调用了wait方法而处于等待中的线程,同时释放该对象锁
  • EntryList(入口队列):用于存放尝试获取对象锁未成功而处于阻塞状态中的线程,当owner释放对象锁时,会唤醒EntryList中的线程来竞争对象锁,成功者成为会owner,失败者则继续等待

Monitor的执行流程:

  1. 第一个进入synchronized代码块的线程,首先尝试获取对象锁,如果锁是空闲状态,则获取到锁,成为owner。如果锁已被其他线程占用,则进入阻塞
  2. 其他访问synchronized代码块的线程尝试获取对象锁未成功,处于阻塞状态,进入EntryList队列,等待对象锁的释放
  3. synchronized代码块执行完毕后,会释放该对象锁,同时唤醒EntryList中阻塞的线程来竞争对象锁,竞争成功的线程成为新的owner,失败的线程则继续等待
目录
相关文章
|
21天前
|
人工智能 Kubernetes Java
回归开源,两位 Java 和 Go 程序员分享的开源贡献指引
Higress是一个基于Istio和Envoy的云原生API网关,支持AI功能扩展。它通过Go/Rust/JS编写的Wasm插件提供可扩展架构,并包含Node和Java的console模块。Higress起源于阿里巴巴,解决了Tengine配置重载及gRPC/Dubbo负载均衡问题,现已成为阿里云API网关的基础。本文介绍Higress的基本架构、功能(如AI网关、API管理、Ingress流量网关等)、部署方式以及如何参与开源贡献。此外,还提供了有效的开源贡献指南和社区交流信息。
256 34
|
27天前
|
Java 程序员 应用服务中间件
【高薪程序员必看】万字长文拆解Java并发编程!(2 2-2)
📌 核心痛点暴击:1️⃣ 面了8家都被问synchronized锁升级?一张图看懂偏向锁→重量级锁全过程!2️⃣ 线程池参数不会配?高并发场景下这些参数调优救了项目组命!3️⃣ volatile双重检测单例模式到底安不安全?99%人踩过的内存可见性大坑!💡 独家亮点抢先看:✅ 图解JVM内存模型(JMM)三大特性,看完再也不怕指令重排序✅ 手撕ReentrantLock源码,AQS队列同步器实现原理大揭秘✅ 全网最细线程状态转换图(附6种状态转换触发条件表)
48 0
|
18天前
|
人工智能 JavaScript Java
Java反射机制及原理
本文介绍了Java反射机制的基本概念、使用方法及其原理。反射在实际项目中比代理更常用,掌握它可以提升编程能力并理解框架设计原理。文章详细讲解了获取Class对象的四种方式:对象.getClass()、类.class、Class.forName()和类加载器.loadClass(),并分析了Class.forName()与ClassLoader的区别。此外,还探讨了通过Class对象进行实例化、获取方法和字段等操作的具体实现。最后从JVM类加载机制角度解析了Class对象的本质及其与类和实例的关系,帮助读者深入理解Java反射的工作原理。
|
27天前
|
网络协议 Java 大数据
【高薪程序员必看】万字长文拆解Java并发编程!(1)
📌 核心痛点暴击:1️⃣ 面了8家都被问synchronized锁升级?一张图看懂偏向锁→重量级锁全过程!2️⃣ 线程池参数不会配?高并发场景下这些参数调优救了项目组命!3️⃣ volatile双重检测单例模式到底安不安全?99%人踩过的内存可见性大坑!💡 独家亮点抢先看:✅ 图解JVM内存模型(JMM)三大特性,看完再也不怕指令重排序✅ 手撕ReentrantLock源码,AQS队列同步器实现原理大揭秘✅ 全网最细线程状态转换图(附6种状态转换触发条件表)
45 0
|
27天前
|
安全 Java 程序员
【高薪程序员必看】万字长文拆解Java并发编程!(2 2-1)
🔥【高薪程序员必看】万字长文拆解Java并发编程!面试官看了直呼内行,90%人不知道的线程安全骚操作!💻🚀《16个高频面试灵魂拷问+底层源码暴击》🔥👉戳这里看如何用1个月经验吊打3年程序员!📌 核心痛点暴击:1️⃣ 面了8家都被问synchronized锁升级?一张图看懂偏向锁→重量级锁全过程!2️⃣ 线程池参数不会配?高并发场景下这些参数调优救了项目组命!3️⃣ volatile双重检测单例模式到底安不安全?99%人踩过的内存可见性大坑!
36 0
|
27天前
|
缓存 安全 Java
【高薪程序员必看】万字长文拆解Java并发编程!(3-1):并发共享问题的解决与分析
活锁:多个线程相互影响对方退出同步代码块的条件而导致线程一直运行的情况。例如,线程1的退出条件是count=5,而线程2和线程3在其代码块中不断地是count进行自增自减的操作,导致线程1永远运行。内存一致性问题:由于JIT即时编译器对缓存的优化和指令重排等造成的内存可见性和有序性问题,可以通过synchronized,volatile,并发集合类等机制来解决。这里的线程安全是指,多个线程调用它们同一个实例的方法时,是线程安全的,但仅仅能保证当前调用的方法是线程安全的,不同方法之间是线程不安全的。
33 0
|
27天前
|
Java 程序员
【高薪程序员必看】万字长文拆解Java并发编程!(3-2):并发共享问题的解决与分析
wait方法和notify方法都是Object类的方法:让当前获取锁的线程进入waiting状态,并进入waitlist队列:让当前获取锁的线程进入waiting状态,并进入waitlist队列,等待n秒后自动唤醒:在waitlist队列中挑一个线程唤醒:唤醒所有在waitlist队列中的线程它们都是之间协作的手段,只有拥有对象锁的线程才能调用这些方法,否则会出现IllegalMonitorStateException异常park方法和unpark方法是LockSupport类中的方法。
40 0
|
Java 程序员
java程序员,如何坚持学习下去?
java程序员,如何坚持学习下去?
154 1
|
Kubernetes 安全 Java
Java程序员2021年应该学习的11项技能
Java程序员2021年应该学习的11项技能
208 0
Java程序员2021年应该学习的11项技能