【高薪程序员必看】万字长文拆解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,失败的线程则继续等待
相关文章
|
5月前
|
IDE Java 编译器
java编程最基础学习
Java入门需掌握:环境搭建、基础语法、面向对象、数组集合与异常处理。通过实践编写简单程序,逐步深入学习,打牢编程基础。
341 1
|
5月前
|
Java
如何在Java中进行多线程编程
Java多线程编程常用方式包括:继承Thread类、实现Runnable接口、Callable接口(可返回结果)及使用线程池。推荐线程池以提升性能,避免频繁创建线程。结合同步与通信机制,可有效管理并发任务。
249 6
|
5月前
|
安全 前端开发 Java
从反射到方法句柄:深入探索Java动态编程的终极解决方案
从反射到方法句柄,Java 动态编程不断演进。方法句柄以强类型、低开销、易优化的特性,解决反射性能差、类型弱、安全性低等问题,结合 `invokedynamic` 成为支撑 Lambda 与动态语言的终极方案。
249 0
|
6月前
|
SQL Java 数据库
2025 年 Java 从零基础小白到编程高手的详细学习路线攻略
2025年Java学习路线涵盖基础语法、面向对象、数据库、JavaWeb、Spring全家桶、分布式、云原生与高并发技术,结合实战项目与源码分析,助力零基础学员系统掌握Java开发技能,从入门到精通,全面提升竞争力,顺利进阶编程高手。
1145 2
|
6月前
|
Java 开发者
Java并发编程:CountDownLatch实战解析
Java并发编程:CountDownLatch实战解析
548 100
|
6月前
|
NoSQL Java 关系型数据库
超全 Java 学习路线,帮你系统掌握编程的超详细 Java 学习路线
本文为超全Java学习路线,涵盖基础语法、面向对象编程、数据结构与算法、多线程、JVM原理、主流框架(如Spring Boot)、数据库(MySQL、Redis)及项目实战等内容,助力从零基础到企业级开发高手的进阶之路。
484 1
|
6月前
|
算法 Java
Java多线程编程:实现线程间数据共享机制
以上就是Java中几种主要处理多线程序列化资源以及协调各自独立运行但需相互配合以完成任务threads 的技术手段与策略。正确应用上述技术将大大增强你程序稳定性与效率同时也降低bug出现率因此深刻理解每项技术背后理论至关重要.
456 16
|
Java 算法 程序员
Java并发问题——乐观锁与悲观锁以及乐观锁的一种实现方式-CAS
首先介绍一些乐观锁与悲观锁: 悲观锁:总是假设最坏的情况,每次去拿数据的时候都认为别人会修改,所以每次在拿数据的时候都会上锁,这样别人想拿这个数据就会阻塞直到它拿到锁。
1682 0
|
Java 算法 程序员
Java并发问题--乐观锁与悲观锁以及乐观锁的一种实现方式-CAS
首先介绍一些乐观锁与悲观锁: 悲观锁:总是假设最坏的情况,每次去拿数据的时候都认为别人会修改,所以每次在拿数据的时候都会上锁,这样别人想拿这个数据就会阻塞直到它拿到锁。
1327 0
|
5月前
|
JSON 网络协议 安全
【Java】(10)进程与线程的关系、Tread类;讲解基本线程安全、网络编程内容;JSON序列化与反序列化
几乎所有的操作系统都支持进程的概念,进程是处于运行过程中的程序,并且具有一定的独立功能,进程是系统进行资源分配和调度的一个独立单位一般而言,进程包含如下三个特征。独立性动态性并发性。
286 1