JAVA中synchronized的原理

目录

‌一、对象头(Object Header)‌

‌Mark Word 结构‌

‌二、Monitor(监视器锁)‌

‌Monitor 核心结构‌

‌锁获取流程‌

‌三、锁升级机制‌

‌1. 偏向锁(Biased Lock)‌

‌2. 轻量级锁(Lightweight Lock)‌

‌3. 重量级锁(Heavyweight Lock)‌

‌四、内存语义‌

‌五、优化机制‌

‌总结‌


在 Java 中,synchronized 关键字的底层实现原理基于 ‌对象头(Object Header)‌、‌Monitor(监视器锁)‌ 和 ‌锁升级机制‌,其设计目标是兼顾线程安全与性能优化。以下是详细原理分析:


一、对象头(Object Header)

每个 Java 对象在内存中分为三部分:‌对象头(Header)‌、‌实例数据(Instance Data)‌ 和 ‌对齐填充(Padding)‌。
synchronized 的锁信息存储在对象头的 ‌Mark Word‌ 中(占 64 位 JVM 下 8 字节)。

Mark Word 结构
锁状态存储内容(64 位示例)
无锁对象哈希码(25bit)、分代年龄(4bit)、偏向模式(1bit)、锁标志位(2bit,值 01)
偏向锁持有偏向锁的线程ID(54bit)、偏向时间戳(2bit)、分代年龄(4bit)、锁标志位(01)
轻量级锁指向栈中锁记录(Lock Record)的指针(62bit)、锁标志位(00)
重量级锁指向 Monitor 对象的指针(62bit)、锁标志位(10)
GC 标记空(用于垃圾回收阶段,锁标志位 11)

特点‌:

  • Mark Word 的内容会随着锁状态动态变化。
  • 锁升级过程:‌无锁 → 偏向锁 → 轻量级锁 → 重量级锁‌。

二、Monitor(监视器锁)

Monitor 是 synchronized 的底层同步机制,每个对象关联一个 Monitor(由 C++ 的 ObjectMonitor 实现)。

Monitor 核心结构
class ObjectMonitor {void*     _header;          // Mark Wordvoid*     _owner;          // 持有锁的线程intptr_t  _recursions;     // 重入次数WaitSet   _WaitSet;        // 等待队列(调用 wait() 的线程)EntryList _EntryList;      // 阻塞队列(竞争锁失败的线程)// ...
};
锁获取流程
  1. 当线程尝试获取锁时,若锁未被占用(_owner 为空),则 CAS 设置 _owner 为当前线程,获取成功。
  2. 若锁已被占用,线程进入 _EntryList 队列阻塞等待,等待锁释放后被唤醒。

三、锁升级机制

JDK 6 后引入 ‌锁膨胀(Lock Inflation)‌ 机制,根据竞争激烈程度动态调整锁状态,优化性能。

1. 偏向锁(Biased Lock)
  • 目的‌:减少无竞争时的同步开销。
  • 触发条件‌:对象未被锁定且未禁用偏向模式。JVM默认延时4s自动开启偏向锁,可通过-XX:BiasedLockingStartupDelay=0 取消延时;如果不要偏向锁,可通过-XX:-UseBiasedLocking =false来设置。
  • 流程‌:
    线程通过 CAS 将 Mark Word 中的线程ID设置为自己的ID,后续可直接进入同步代码块,无需竞争。
  • 撤销‌:当其他线程尝试获取锁时,偏向锁会升级为轻量级锁。
2. 轻量级锁(Lightweight Lock)
  • 目的‌:减少多线程交替执行时的锁竞争开销。
  • 流程‌:
    线程在栈帧中创建 Lock Record,通过 CAS 将 Mark Word 复制到 Lock Record,并尝试将 Mark Word 指向 Lock Record。
    若成功则获取锁,失败则自旋重试或升级为重量级锁。
3. 重量级锁(Heavyweight Lock)
  • 触发条件‌:自旋失败或竞争激烈。当线程锁竞争情况严重,jdk使用适应性自旋,某个达到最大自旋次数的线程(默认为10次),会将轻量级 锁升级Q为重量级锁(通过CAS修改所标志位,但不修改持有ID)。当后序的线程尝试获取锁时,就将自己挂起,等待被唤醒。重量级锁将控制权交给了操作系统,有操作系统来负责线程间的调度和状态变换,会出现频繁的对线程运行状态的切换,线程的挂起和唤醒,从而消耗大量的系统资源。
  • 机制‌:通过操作系统互斥量(Mutex Lock)实现线程阻塞和唤醒,涉及用户态到内核态的切换,性能开销大。

四、内存语义

synchronized 通过 ‌锁的获取与释放‌ 实现内存可见性:

  1. 进入同步块‌(获取锁):强制从主内存重新加载变量。
  2. 退出同步块‌(释放锁):强制将修改刷新到主内存。

五、优化机制

JDK 6 后引入多项优化:

  • 锁消除(Lock Elimination)‌:
  •  JIT 编译器通过逃逸分析,移除不可能存在竞争的锁。‌对象未逃逸‌:锁对象的作用域仅限于当前方法,不会被其他线程访问。无实际竞争‌:同步操作在多线程环境下没有实际意义。举个例子:

    逃逸分析‌:StringBuffer 对象 sb 是方法内的局部变量,‌未逃逸‌到 concatStrings 方法外(不会被其他线程访问)。

    锁消除‌:JIT 编译器会移除 sb.append() 内部的 synchronized 锁,优化后的代码等效于使用 StringBuilder(非线程安全类)。

    public class LockEliminationDemo {// 方法内部的局部变量,未逃逸到方法外public static String concatStrings(int n) {StringBuffer sb = new StringBuffer();  // 锁对象 sb 未逃逸for (int i = 0; i < n; i++) {sb.append(i);  // append() 方法有 synchronized 修饰}return sb.toString();}public static void main(String[] args) {String result = concatStrings(10000);System.out.println(result);}
    }
    

  • 锁粗化(Lock Coarsening)‌:将多次连接在一起的加锁、解锁操作合并为一次,将多个连续的锁扩展成一个范围更大的锁。例子:
    public void example() {Object lock = new Object();synchronized (lock) {// 操作1}synchronized (lock) {  // 同一锁对象// 操作2}
    }******************‌优化后‌(合并为一个同步块)**********************
    public void example() {Object lock = new Object();synchronized (lock) {// 操作1// 操作2}
    }
    
    for (int i = 0; i < 1000; i++) {synchronized (lock) {x += i;  // 循环内重复加锁}
    }************优化后‌(锁提升到循环外)*******************
    synchronized (lock) {for (int i = 0; i < 1000; i++) {x += i;  // 单次加锁}
    }
    
  • 自适应自旋(Adaptive Spinning)‌:根据历史自旋成功率动态调整自旋次数。从轻量级锁获取的流程中我们知道,当线程在获取轻量级锁的过程中执行CAS操作失败时,是要通过自旋来获取重量级锁的。问题在于,自旋是需要消耗CPU的,如果一直获取不到锁的话,那该线程就一直处在自旋状态,白白浪费CPU资源。解决这个问题最简单的办法就是指定自旋的次数,例如让其循环10次,如果还没获取到锁就进入阻塞状态。但是JDK采用了更聪明的方式-一适应性自旋,简单来说就是线程如果自旋成功了,则下次自旋的次数会更多,如果自旋失败了,则自旋的次数就会减少。

总结

  • 底层核心‌:通过对象头的 Mark Word 和 Monitor 实现锁状态管理。
  • 锁升级‌:根据竞争强度动态调整锁类型(偏向锁 → 轻量级锁 → 重量级锁)。
  • 设计目标‌:在无竞争时降低开销,在竞争激烈时保证线程安全。
  • 适用场景‌:适合需要保证原子性、可见性和有序性的复杂同步逻辑。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/41711.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

如何快速下载并安装 Postman?

从下载、安装、启动 Postman 这三个方面为大家详细讲解下载安装 Postman 每一步操作&#xff0c;帮助初学者快速上手。 Postman 下载及安装教程(2025最新)

计算机网络高频(三)UDP基础

计算机网络高频(三)UDP基础 1.UDP的头部格式是什么样的?⭐ UDP 头部具有以下字段: 源端口(Source Port):16 位字段,表示发送方的端口号。目标端口(Destination Port):16 位字段,表示接收方的端口号。长度(Length):16 位字段,表示 UDP 数据报(包括头部和数据部…

2024年MathorCup数学建模B题甲骨文智能识别中原始拓片单字自动分割与识别研究解题全过程文档加程序

2024年第十四届MathorCup高校数学建模挑战赛 B题 甲骨文智能识别中原始拓片单字自动分割与识别研究 原题再现&#xff1a; 甲骨文是我国目前已知的最早成熟的文字系统&#xff0c;它是一种刻在龟甲或兽骨上的古老文字。甲骨文具有极其重要的研究价值&#xff0c;不仅对中国文…

【深度学习的数学】导数

导数的定义。好像是从极限开始的。比如说&#xff0c;函数f(x)在点xa处的导数&#xff0c;就是当h趋近于0时&#xff0c;[f(ah) - f(a)]除以h的极限&#xff0c;对吧&#xff1f;公式应该是这样的&#xff1a;f’(a) lim_{h→0} [f(ah) - f(a)] / h。这个极限如果存在的话&…

word文件转换为Markdown格式

目录 一、前言1.1、poi-ooxml、docx4j、aspose-words对比二、poi-ooxml技术实现一、前言 顺应时代技术的变更及高效协同理念的影响,非结构化信息展示、存储、应用等也由传统文档向在线协同文档的演变,类似腾讯在线文档。   目前大多数在线文档支持的是Markdown格式,因此这…

【Hugging Face 开源库】Diffusers 库 —— 扩散模型

Diffusers 的三个主要组件1. DiffusionPipeline&#xff1a;端到端推理工具__call__ 函数callback_on_step_end 管道回调函数 2. 预训练模型架构和模块UNetVAE&#xff08;Variational AutoEncoder&#xff09;图像尺寸与 UNet 和 VAE 的关系EMA&#xff08;Exponential Moving…

langserve搭建方法

文章目录 安装 langserver安装 langchain-cli创建langserve脚手架使用poetry管理包 安装 langserver pip install langserve安装 langchain-cli pip install langchain-cli创建langserve脚手架 langchain app new 项目名后续交互界面全回车&#xff0c;接着cd到 项目名 目录…

网络基础-路由器和交换机工作配置

三、路由器和交换机的工作原理配置以及华为体系下的小型网络的搭建 3.1路由基础 3.1.1数据转发 通过链路层交换机和网络层路由器进行数据转发 交换机&#xff08;链路层&#xff09;mac地址表的数据转发路由器&#xff08;网络层&#xff09; ip路由表的数据转发 隔离广播域…

mysql高级,mysql体系结构,mysql引擎,存储过程,索引,锁

1.mysql体系结构 1&#xff09; 连接层 主要完成一些类似于连接处理、授权认证、及相关的安全方案。在该层上引入了线程池的概念&#xff0c;为通过认证安全接入的客户端提供线程。同样在该层上可以实现基于SSL的安全链接。服务器也会为安全接入的每个客户端验证它所具有的操作…

Unity高清渲染管线

Unity高清渲染管线——1 unity高清渲染管线是渲染管线的一种&#xff0c;在看完《创造高清3D虚拟世界》这本书的前两章以及第三张第二小节后终于对unity的高清渲染管线也是有了一个初步的认知&#xff0c;以下是我个人理解仅作参考&#xff1a; unity高清渲染管线项目模板比起…

Python基础语法元素(学习笔记)

实例1&#xff1a;温度转换 # TempConvert.py #为单行注释 多行注释为: 这里写内容 TempStr input("请输入带有符号的温度值&#xff1a;") if TempStr[-1] in [F,f] :C (eval(TempStr[0:-1])-32)/1.8print("转换后的温度是{:.2f}C".format(C)) e…

C++20 中的std::c8rtomb和 std::mbrtoc8

文章目录 1. 引言2. std::c8rtomb 函数详解3. std::mbrtoc8 函数详解4. 使用示例5. 注意事项6. 总结 1. 引言 C20 标准引入了对 UTF-8 编码的更好支持&#xff0c;其中包括两个重要的函数&#xff1a;std::c8rtomb 和 std::mbrtoc8。这两个函数分别用于将 UTF-8 编码的字符转换…

数据可视化TensorboardX和tensorBoard安装及使用

tensorBoard 和TensorboardX 安装及使用指南 tensorBoard 和 TensorBoardX 是用于可视化机器学习实验和模型训练过程的工具。TensorBoard 是 TensorFlow 官方提供的可视化工具&#xff0c;而 TensorBoardX 是其社区驱动的替代品&#xff0c;支持 PyTorch 等其他框架。以下是它…

flutter-实现瀑布流布局及下拉刷新上拉加载更多

文章目录 1. 效果预览2. 结构分析3. 完整代码4. 总结 1. 效果预览 在 Flutter 应用开发中&#xff0c;瀑布流布局常用于展示图片、商品列表等需要以不规则但整齐排列的内容。同时&#xff0c;下拉刷新和上拉加载更多功能&#xff0c;能够极大提升用户体验&#xff0c;让用户方…

【day2】数据结构刷题 栈

一 有效的括号 给定一个只包括 (&#xff0c;)&#xff0c;{&#xff0c;}&#xff0c;[&#xff0c;] 的字符串 s &#xff0c;判断字符串是否有效。 有效字符串需满足&#xff1a; 左括号必须用相同类型的右括号闭合。左括号必须以正确的顺序闭合。每个右括号都有一个对应的…

YAML是什么?

YAML&#xff08;YAML Ain’t Markup Language&#xff09;是一种以数据为中心、高度可读的序列化语言&#xff0c;广泛应用于配置文件、数据交换和自动化工具中。以下从多个维度对其进行全面解析&#xff1a; 1. 定义与历史演变 全称与定位&#xff1a; YAML的全称最初为“Yet…

熔断降级(Sentinel解决)

问题概述 在微服务架构中一定要预防微服务雪崩问题&#xff0c;微服务雪崩问题就是指在微服务架构中&#xff0c;当一个服务出现故障时&#xff0c;由于服务之间的依赖关系&#xff0c;故障可能会传播到其他服务&#xff0c;从而导致了大规模的服务失败&#xff0c;系统无法正…

反序列化漏洞

前提概要 本文章主要用于分享反序列化漏洞基础学习&#xff0c;以下是对反序列化漏洞的一些个人解析&#xff0c;请大家结合参考其他文章中的相关信息进行归纳和补充。 反序列化漏洞描述 反序列化漏洞是指程序在对输入的字节流进行反序列化时&#xff0c;因缺乏充分的验证和过…

吐血整理:Air8201如何使用LuatOS进行电源管理功能!

在物联网应用场景中&#xff0c;设备续航能力直接影响其部署成本与运维效率。LuatOS操作系统通过软件层面的精细化控制&#xff0c;为Air8201提供了灵活且高效的电源管理策略。本文将从系统架构、API接口、实战配置三个维度&#xff0c;解析如何利用LuatOS实现Air8201的智能电源…

STM32学习笔记之存储器映射(原理篇)

&#x1f4e2;&#xff1a;如果你也对机器人、人工智能感兴趣&#xff0c;看来我们志同道合✨ &#x1f4e2;&#xff1a;不妨浏览一下我的博客主页【https://blog.csdn.net/weixin_51244852】 &#x1f4e2;&#xff1a;文章若有幸对你有帮助&#xff0c;可点赞 &#x1f44d;…