1、为什么要有内存模型?

(1)缓存一致性问题
计算机在执行程序的时候,每条指令都是在CPU中执行,执行时的数据是存放在物理内存当中的。
刚开始时CPU直接从主内存中读写数据,但随着CPU技术的不断发展,CPU的执行速度越来越快;而内存技术并没有太大的变化。
所以从内存中读取和写入数据的过程和CPU的执行速度比较来差距就会越来越大,这就导致CPU每次操作内存都要耗费很多等待时间。
于是就在CPU和内存之间增加告诉缓存。
那么程序的执行过程就变成了:程序在运行过程中,会将运算需要的数据从主内存复制一份到CPU的高速缓存中。CPU进行计算时就可以直接从它的高速缓存读取数据和向其中写入数据,当运算结束之后,再将高速缓存中的数据写入到主内存。
随着CPU的能力不断提升,一层缓存慢慢无法满足要求了,就逐渐的衍生出多级缓存。
按照数据读取顺序和与CPU结合的紧密程度,CPU缓存可以分为一级缓存(L1),二级缓存(L2)甚至部分CPU具有三级缓存(L3),每级缓存中所存储的全部数据都是下级缓存的一部分。
当有了多级缓存程序执行就变成了:当CPU读取一个数据时,首先从一级缓存中查找,如果没有再从二级缓存中查找,如果还是没有再从三级缓存或者主内存中查找。
在CPU和主内存直接增加缓存,在多线程场景下会存在缓存一致性问题。

(2)处理器优化问题
程序执行时会出现处理器优化问题。
为了使处理器内部的运算单元能够被充分利用,处理器可能会对输入代码进行乱序执行处理。

(3)指令重排问题
在编译时,也会做成类似的优化。比如Java虚拟机的即时编译器(JIT)也会做指令重排。

针对以上的问题,在并发编程时抽象定义为原子性问题、可见性问题和有序性问题。
也就是说在并发编程中,为了保证数据安全,需要满足一下三个特性:
原子性:在一个操作中,CPU不可以中途暂停然后再调度,即不被中断操作,要么执行完成,要么不执行。
可见性:是指当多个线程访问同一个变量时,一个线程修改了这个变量的值,其他线程能够立即看到修改后的值。
有序性:程序执行的顺序按照代码先后顺序执行。
实际上缓存一致性问题其实就是可见性问题。而处理器优化是可以导致原子性问题的。指令重排即会导致有序性问题。

2、什么是内存模型?

为了解决以上三个问题,建立了一个重要的概念–内存模型。
为了保证并发场景下共享内存的正确性(原子性、可见性、有序性),内存模型定义了共享内存系统中多线程程序读写操作行为的规范。
内存模型解决并发问题主要采用两种方式:
(1)限制处理器优化
(2)使用内存屏障

3、什么是Java内存模型?

不同的编程语言,在实现都各不相同。
Java内存模型(Java Memory Model,JMM) 就是一种符合内存模型规范的,屏蔽了各种硬件和操作系统的访问差异的,保证了Java程序在各种平台下对内存的访问都能保证效果一致的机制和规范。
Java内存模型主要是由JSR-133:JavaTM Memory Model and Thread Specification 描述。

Java并发编程(三):Java内存模型
Java内存模型规定所有的变量都存储在住内存中,每条线程还有自己的工作内存。
线程的工作内存中保存了该线程中用到的变量的主内存副本拷贝,线程对变量的所有操作都必须在工作内存中进行,而不能直接读写主内存。
线程的工作内存为线程私有,其他线程不能直接访问。线程间变量的传递需要自己的工作内存和主内存直接进行数据同步。

概念总结
这里的主内存和工作内存,可以类比成计算机内存模型中的主存和缓存。
而主内存和工作内存与 JVM 内存结构中的 Java 堆、栈、方法区等并不是同一个层次的内存划分,无法直接类比。
《深入理解Java虚拟机》中认为:主内存和工作内存勉强对应起来的话,从变量、主内存、工作内存的定义来看,主内存主要对应于 Java 堆中的对象实例数据部分。而工作内存则对应于虚拟机栈中的部分区域。

总结来看,JMM是一种规范,用于解决并发场景中,出现的缓存、处理器优化、指令重排等带来的问题。
目的是为了保证并发编程场景中原子性、可见性、有序性

相关文章: