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

Java内存模型

作者头像
每天学Java
发布2020-06-02 09:57:19
4920
发布2020-06-02 09:57:19
举报
文章被收录于专栏:每天学Java每天学Java每天学Java

Java内存模型(Java Memory Model,JMM)的定义是Java虚拟机试图实现Java程序在各种平台下都能达到一致的内存访问效果。

我们都知道指令的执行是在CPU中进行的,而数据是存放物理内存中的,但是CPU的处理速度一般比内存快的多,于是我们引入高速缓存,它的存储交互很好的解决了CPU与内存之间的矛盾。与此同时摩尔定律表明了单核CPU的主频不可能无限制的增长,要想提升新能,需要多个处理器协同工作,也就是多核。高速缓存和多核的引入,也带来了一些问题:比如缓存一致性问题(当多个处理器运算都涉及到同一块内存区域的时候,就有可能发生缓存不一致的现象)。说到这里,要表达什么呢?在多核系统中,不同系统如何解决高速缓存和多核带来的问题呢,那就是内存模型。内存模型定义了共享内存系统中多线程程序读写操作行为的规范,它保证内存的正确性(可见性、有序性、原子性)。

01

Java内存模型

上面对于内存模型的描述可能会有不正之处,希望大家带着批判的眼光去看这篇文章。

内存模型是为了保证共享内存的正确性,在不同平台上内存模型也是存在差异的,相同的程序在不同平台上,其并发的效果也是不同的。因此Java虚拟机试图定义Java内存模型去屏蔽各种内存和操作系统的内存访问差异,以实现Java程序在各种平台下都能达到一致的内存访问效果。

在JDK1.5发布后,Java内存模型已经成熟和完善起来了。所以我们提到的JMM,一般指的是JDK 5 开始使用的新的内存模型

Java内存模型规定了所有的变量都存储在主内存中,每条线程还有自己的工作内存,线程的工作内存中保存了该线程中是用到的变量的主内存副本拷贝,线程对变量的所有操作都必须在工作内存中进行,而不能直接读写主内存。不同的线程之间也无法直接访问对方工作内存中的变量,线程间变量的传递均需要自己的工作内存和主存之间进行数据同步进行。

线程,工作内存,主内存三者交互关系如图:

JMM就作用于工作内存和主存之间数据同步过程。他规定了如何做数据同步以及什么时候做数据同步。

02

volatile型变量

关于volatile型变量,我们平时听的很多,我们大概都知道被该关键字修饰的变量保证对所有线程的可见性,意思是某一线程修改这个变量的值,对其他线程是可见的,这好像跟我们在上面描述的JMM不同,因为所有变量被线程修改,都是在工作内存中进行的,对于其他线程是不可见的,为什么volatile能打破这个规则呢?实际上一个volatile变量,任意线程都是能看到对这个volatile变量最后的写入。 当写一个volatile变量时,JMM会把该线程对应的本地内存中的共享变量刷新到主内存, 当读一个volatile变量时,JMM会把该线程对应的本地内存置为无效。线程接下来将从主内存中读取共享变量。这就是为什么volatile变量对所有线程可见。

既然volatile型号变量的写操作保证了可见性,那么它是不是就不存在一致性的问题。在Java语言里面运算不是原子操作,在并发情况下volatile同样会出现不安全问题。这是为什么呢?我没想一下i++这个操作,如果两个线程A,B对i执行i++操作,如果线程A从主存中读取i=0进了缓存,然后把数据读入寄存器,之后时间片用完了,然后线程2也从内存中读取i进缓存,因为线程A还未执行写操作,内存屏障是插入在写操作之后的指令,意味着还未触发这个指令,所以缓存行是不会失效的。然后线程2执行完毕,内存中i=1,然后线程1又开始执行,然后将数据写回缓存再写回内存,结果还是1。

所以在不符合以下两条规则的运算场景中,我们仍然要通过加锁来保证原子性:

1.运算结果并不依赖变量的当前值,或者能够确保只有单一的线程修改变量的值

2.变量不需要与其他的状态变量共同参与不变约束。

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

本文分享自 每天学Java 微信公众号,前往查看

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

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

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档