随着CPU
和内存的发展速度差异的问题,导致
CPU
的速度远快于内存,所以现在的
CPU
加入了高速缓存,高速缓存一般可以分为L1
、
L2
、
L3
三级缓存。基于上面的例子我们知道了这导致了缓存一致性的问题,所以加入了缓存一致性协议,同时导致了内存可见性的问题,而编译器和CPU
的重排序导致了原子性和有序性的问题,JMM
内存模型正是对多线程操作下的一系列规范约束,因为不可能让陈雇员的代码去兼容所有的CPU
,通过
JMM
我们才屏蔽了不同硬件和操作系统内存的访问差异,这样保证了Java
程序在不同的平台下达到一致的内存访问效果,同时也是保证在高效并发的时候程序能够正确执行。
原子性:
Java
内存模型通过
read
、
load
、
assign
、
use
、
store
、
write
来保证原子性操作,此外还有
lock
和
unlock
,直接对应着
synchronized
关键字的
monitorenter
和
monitorexit
字节码指令。
可见性:可见性的问题在上面的回答已经说过,
Java
保证可见性可以认为通过
volatile
、
synchronized
、
final
来实现。
有序性:由于处理器和编译器的重排序导致的有序性问题,
Java
通过
volatile
、
synchronized
来保
证。
happen-before
规则
虽然指令重排提高了并发的性能,但是Java
虚拟机会对指令重排做出一些规则限制,并不能让所有的指令都随意的改变执行位置,主要有以下几点:
1.
单线程每个操作,
happen-before
于该线程中任意后续操作
2. volatile
写
happen-before
与后续对这个变量的读
3. synchronized
解锁
happen-before
后续对这个锁的加锁
4. final
变量的写
happen-before
于
final
域对象的读,
happen-before
后续对
final
变量的读
5.
传递性规则,
A
先于
B
,
B
先于
C
,那么
A
一定先于
C
发生
说了半天,到底工作内存和主内存是什么?
主内存可以认为就是物理内存,Java
内存模型中实际就是虚拟机内存的一部分。而工作内存就是CPU缓存,他有可能是寄存器也有可能是
L1\L2\L3
缓存,都是有可能的