当前位置: 首页 > news >正文

JMM详解

JMM详解

引子

众所周知,在计算机中,数据主要存储在硬盘中,但是,硬盘的读写速度相对于CPU执行指令的速度太慢太慢了,于是,我们会添加读写速度更快的内存(因为内存读写速度比较快,所以也比较贵,如果直接用内存取代磁盘的话,大部分人都承担不起这个价钱)来作为硬盘的缓存。数据先从磁盘读取到内存中,内存可以较高的效率与CPU交互,这样也就提高了计算机的效率。

当内存中的数据更新时,需要写回到磁盘中,这需要时间,也就导致了在这段时间内,磁盘与内存间数据的不一致现象。

当有多个线程时,有的现场从内存中读值,有的线程从磁盘中读值,或者多个线程同时修改内存中的值,结果导致操作被覆盖,等等。此外,现代编译器为了提高程序运行效率,会对代码进行重排序,现代CPU为了提高指令运行效率,也会对指令进行重排序。这些情况都可能导致多线程共享数据数据的不一致现象(重排序只保证单线程安全)。

这就需要一个规范(称之为“内存模型”)来解决这些混乱的情况,不同操作系统会有不同的规范。Java因为要保证跨平台性,所以不能直接使用某个操作系统现成的规范,需要有自己的规范,由JVM负责适配各操作系统。Java的这套规范,就叫做JMM(Java Memory Model :Java内存模型)

JMM

JMM是用来解决Java并发编程问题的一个规范。

JMM不是具体的技术实现,不同版本的Java可以用不同的方式实现这个规范。

JMM也利用了缓存机制来提效,所有共享变量都存在主内存中(可能是用操作系统的内存来实现),线程需要将共享变量读取到自己独立的工作内存中(可能是用操作系统的CPU高速缓存来实现),操作完成后再写回主内存中。

想要保证并发编程的安全性,就要解决以下问题:

  • 一个线程对共享变量的操作是个不可分割的整体,不可中断

    错误情况:

    初始 i = 0 线程A: 读 i -> 0 线程B: 读 i -> 0 线程A: i+1 -> 1 线程B: i+1 -> 1 线程A: 写回 i=1 线程B: 写回 i=1 最终 i=1 (本该是2)

    正确情况:

    初始 i = 0 --------------- | 线程A: 读 i -> 0 不可分割的整体| 线程A: i+1 -> 1 | 线程A: 写回 i=1 --------------- | 线程B: 读 i -> 1 不可分割的整体| 线程B: i+1 -> 2 | 线程B: 写回 i=2 --------------- 最终 i=2

    因为线程A对i的操作是个不可分割的整体,所以线程B不能穿插进去。

  • 一个线程对共享数据的操作结果可以立刻被其它线程看见

    对于上面的情况,可能线程A写回i=1到主存中,但是,

    可能主存也有多级缓存,线程A写回到2级主存中,但线程B却从1级主存中读值;

    可能主存虽然拿到线程A写回的新值了,但是还没来得及更新旧值;

    可能线程B之前缓存过旧值,这次没从主存中读取,而是从自己的缓存中读取。

    所以,必须要保证线程A写回的值,线程B能够立即看见。

  • 一个线程对共享数据的操作的重排序不能影响其他线程

    试想这种情况:

    初始 i = 0 线程A: 读 i -> 0 线程A: i=1 线程A: i=2 线程A: 写回 i=2 线程B: 读 i -> 2 线程B: i+1 -> 3 线程B: 写回 i=3 最终 i=3

    如果发生指令重排序,将线程A: i=1线程A: i=2的顺序反过来执行,那么最终结果就是2。

总结一下,如果想要保证并发编程的安全性,就需要保证三大特性:

  • 原子性:一系列操作组成不可分割的整体,不可被中断。

  • 可见性:一个线程对共享变量的修改,其它线程可以立即看见。

  • 有序性:指令重排序不能影响多线程的结果。

那么,Java通常是如何实现这三大特性的呢?

原子性

在Java中,可以通过synchronizedLock加锁来实现原子性:

加锁的代码块,在同一时刻只有一个线程能访问。其他线程必须等待锁被释放后,并且尝试获取到锁后,才能访问该代码块。也就不会被其它线程穿插进来,中断执行。

也可以通过原子类来实现原子性:

原子类(如:AtomicInteger、AtomicLong)利用CPU提供的原子级指令*CAS(Compare And Swap)*来实现原子性,CPU会保证不会有其它指令穿插进来。

可见性

volatile修饰共享变量,可以保证被修改的共享变量立刻写回主存,读取时只从主存中读取。

因为原子类内部也是用volatile来修饰变量,所以原子类也可以保证可见性。

但是,volatile只能修饰单个变量,所以也只能保证单个变量的可见性。如果要组合操作多个共享变量,需要使用synchronizedLock加锁,他们也可以保证被修改的共享变量立刻写回主存,读取时只从主存中读取。

有序性

JMM有一个happens-before原则,要求某个操作必须发生在某个操作之前,从而限制了指令重排序,保证了有序性。

在Java中,volatilesynchronizedLock都实现了happens-before原则,会在操作前后加入内存屏障,不允许内存屏障前的指令和内存屏障后的乱序,从而保证有序性。

http://www.jsqmd.com/news/449675/

相关文章:

  • 小白必看!Stable Diffusion v1.5 WebUI界面详解与参数设置指南
  • 【计算机毕业设计】基于Django的可视化人工智能科普平台+LW
  • ble mesh的基础知识 ---Provisioner
  • BS101智能垃圾分拣设备:西门子1200PLC与TP900触摸屏博途V15.1程序全套包
  • 集成墙板,性价比之选解析
  • VideoAgentTrek-ScreenFilter行业落地:在线教育平台课件画面智能标注
  • Spring Boot (API) + PostgreSQL联动监控
  • 横评后发现!王者级的AI论文写作软件 —— 千笔写作工具
  • 计算机网络知识应用:优化卡证检测模型API的网络传输性能
  • 为什么很多 PCB 项目一开始报价就错了--工程评估阶段最容易忽略的 6 个成本变量
  • Qwen1.5-1.8B GPTQ一键部署教程:Python环境快速配置指南
  • 上海智推时代 GEO 合作指南:2026 年 3 月最新官方对接方式 - 速递信息
  • 海外GEO系统哪家靠谱?亲测5家复盘分享
  • 宝塔面板Linux面板安装命令
  • LDAP Injection
  • freertos开发空气检测仪之综合展示
  • Nano-Banana入门必看:knolling美学三大法则(对称/留白/色彩秩序)AI实现
  • 手把手教你用Qwen3-ForcedAligner-0.6B:上传音频即出字幕,无需任何代码
  • IRBCRB15000_New_GoFa-2v2国外机器人防护服注意事项解析与避坑指南
  • 阿里云主机无法打开宝塔面板的解决方法—放行安全组教程
  • 人工智能+AI的蔬菜水果商城批发系统的设计与实现
  • 程序的运营AI公司四川谦与谦寻科技有限公司获客系统开发商
  • 云测试平台实战:Jenkins集成与性能优化秘籍
  • CSV可视化图片列HTML渲染
  • SQL优化全攻略:从索引策略到Explain实战解析
  • 《创业之路》-890- 法律的本质
  • 说说昇顺交通设施厂,产品靠谱吗,在山东、北京、天津地区口碑如何? - 工业品牌热点
  • 堆与完全二叉树的Python实现
  • 应急电源车智慧远程管理平台方案
  • 文墨共鸣企业实操:内容审核中‘同义替换’风险文本自动识别方案