前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >浅析Java内存模型(JMM)

浅析Java内存模型(JMM)

作者头像
叫我阿柒啊
发布2022-05-09 20:21:56
3160
发布2022-05-09 20:21:56
举报

背景

随着cpu由单核变成多核,又有了超线程。所以就会出现这样的问题,多核cpu在各自的缓存处理数据后,当同步数据到同一块主内存时,无法确定以谁的缓存数据为准。所以为了解决cpu缓存一致性的问题,特地制定了一些操作协议,例如MSI、MOSI、Firefly等。而在这些操作协议下,对特定的内存或高速缓存进行读写访问的过程,就是内存模型。不同架构(ARM/X86等)的物理机有不同的内存模型。

为了屏蔽不同架构的机器上的内存访问差异,让Java程序在各种平台下都能达到一致的内存访问效果,所以Java也制定了一套内存操作协议,即Java内存模型。JMM保证了多线程下变量的缓存一致性。

工作内存和主内存

JMM主要目标是定义程序中各个变量的访问规则,即在JVM中将变量存储到内存和从内存中取出变量这样的底层细节,来实现缓存一致性。JMM在定义上将内存分为主内存和工作内存,主内存对应Heap,属于线程公有,工作内存对应虚拟机栈,属于每个线程私有。

同时JMM对内存中的变量做了以下规定:

  1. 规定所有的变量都存储在主内存中,线程不能直接读写主内存中的变量
  2. 每个线程有自己的工作内存,对变量的读取、赋值等必须在工作内存中进行
  3. 线程之间值的传递都需要通过主内存来完成。工作内存无法互相访问
  4. 工作内存保存了线程用到的变量的主内存副本拷贝

原子操作

既然JMM规定了变量在主内存和工作内存中如何传输和操作,同时也提供了八个原子指令来具体实现细节。

对于上面的八个操作指令,JMM也指定了一些规则:

  1. read/load、store/write必须成对出现
  2. 不允许线程丢弃最近的assign操作,即工作内存中变量修改之后必须同步到主内存
  3. 不允许线程无原因地(没有assign操作)将变量工作内存同步到主内存
  4. 变量只能在主内存中诞生,并且必须在工作内存中初始化才能使用。即use、store之前必须经过load和assign
  5. 一个变量在同一时刻只能被一个线程lock。但一个线程可以lock多次。几次lock,只有对应次数的unlock变量才能解锁
  6. 一个变量被lock后,会清空所有工作内存中此变量的值。再次使用需要重新load、assign来初始化
  7. 一个变量未被lock,则不允许对它执行unlock,也不允许unlock其他线程lock的变量
  8. 一个变量unlock前,必须先把此变量同步到主内存中(store、write)

如何理解这八条规则和八条指令?

指令中的lock/unlock是让一个变量被一个线程独享,其他六个指令都是变量在工作内存中的赋值和传输操作. 规则中最核心的就是第六条。JMM的出现是为了当一个线程对变量修改时,其他线程停止修改,并能获取最新的值来进行操作,从而保证数据的一致性。而lock可以让一个变量只能被一个线程修改,而且让其他线程的工作内存此变量的值失效,想用此变量必须通过指令来获取新的值。

应用场景

synchronize底层是由monitorenter/monitorexit来实现的,就相当于lock/unlock。在synchronize的作用范围内,只能一个线程进行操作,其他线程工作内存中的变量会失效,当此线程修改完unlock的时候,其他线程会重新加载变量最新值来进行操作,从而保证数据的一致性。volatile关键字也通过内存屏障实现了lock/unlock的功能。

结语

今天看了大冰的《啊 2.0》第一二章,感慨良多。希望我们都可以在各自的地方努力成长。

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2020-08-22,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 入门到放弃之路 微信公众号,前往查看

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

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

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 背景
  • 工作内存和主内存
  • 原子操作
  • 应用场景
  • 结语
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档