JAVA 内存模型

Posted 皓月行空

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了JAVA 内存模型相关的知识,希望对你有一定的参考价值。

环境:1.7.0.17

坚持一下,把源码看完,勤奋一点,不要在懒惰了,你已经落下别人很多了

本文主要介绍JAVA的内存模型


一直一下对JAVA 的内存模型不甚了解,直到看见了下面一张图


其中,主内存是线程共享的区域,工作内存 是线程私有的区域。工作内存是JMM的一个抽象概念,并不真实存在。它涵盖了缓存,写缓冲区,寄存器以及其他的硬件和编译器优化。

1、happen-before原则

从jdk5开始,java使用新的JSR-133内存模型,基于happens-before的概念来阐述操作之间的内存可见性。

在JMM中,如果一个操作的执行结果需要对另一个操作可见,那么这两个操作之间必须要存在happens-before关系,这个的两个操作既可以在同一个线程,也可以在不同的两个线程中。

程序员密切相关的happens-before规则如下:

  1. 程序顺序规则:一个线程中的每个操作,happens-before于该线程中任意的后续操作。
  2. 监视器锁规则:对一个锁的解锁操作,happens-before于随后对这个锁的加锁操作。
  3. volatile域规则:对一个volatile域的写操作,happens-before于任意线程后续对这个volatile域的读。
  4. 传递性规则:如果 A happens-before B,且 B happens-before C,那么A happens-before C。

注意:两个操作之间具有happens-before关系,并不意味前一个操作必须要在后一个操作之前执行!仅仅要求前一个操作的执行结果,对于后一个操作是可见的,且前一个操作按顺序排在后一个操作之前。

2、指令重排序

在计算机执行指令的顺序在经过程序编译器编译之后形成的指令序列,一般而言,这个指令序列是会输出确定的结果;以确保每一次的执行都有确定的结果。但是,一般情况下,CPU和编译器为了提升程序执行的效率,会按照一定的规则允许进行指令优化,在某些情况下,这种优化会带来一些执行的逻辑问题,主要的原因是代码逻辑之间是存在一定的先后顺序,在并发执行情况下,会发生二义性,即按照不同的执行逻辑,会得到不同的结果信息。

(1)数据依赖性;如果两个操作访问同一个变量,其中一个为写操作,此时这两个操作之间存在数据依赖性

(2)as-if-serial:不管怎么重排序,单线程下的执行结果不能被改变


3、内存屏障Memory barrier

内存屏障,又称内存栅栏,是一个CPU指令,基本上它是一条这样的指令:

(1)保证特定操作的执行顺序.

(2)影响某些数据(或则是某条指令的执行结果)的内存可见性。

编译器和CPU能够指令重拍,保证最终相同结果,尝试优化性能。

插入一条Memory Barrier会告诉编译器和CPU;不管什么指令都不能和这条Memory Barrier指令重排序。


4、volatile

如果变量是volatile修饰的,JMM会在写入这个字段之后插入一个WriteBarrier指令,并在读这个字段之前插入一个ReadBarrier指令。

这意味着,如果写入一个volatile变量,就可以保证:

(1)一个线程写入变量a后,任何线程访问该变量都会拿到最新值。

(2)在写入变量a之前的写入操作,其更新的数据对于其他线程也是可见的。因为MemoryBarrier会刷出cache中的所有前的写入


以上是关于JAVA 内存模型的主要内容,如果未能解决你的问题,请参考以下文章

C# volatile 变量:内存栅栏 VS。缓存

线程之间的内存栅栏/屏障如何与其他线程中的栅栏/屏障交互?

内存栅栏:获取/加载和释放/存储

内存栅栏

这里需要内存栅栏吗?

对java内存模型的认识