cpu缓存模型
上图中的cpu缓存实际上应该数据cpu中,为了方便展示而画在外面,cpu的运算速度大大的快于主内存,所以为了屏蔽掉这种差异,会使用cpu高速缓存,使cpu在获取数据时先从高速缓存中获取。
java内存模型
java线程内存模型跟cpu缓存模型类似,是基于cpu缓存模型来建立的,java线程内存模型是标准化的,屏蔽掉了底层不同计算机的区别
java内存模型数据原子操作
- read(读取):从主内存读取数据
- load(载入):将主内存读取到的数据写入到工作内存
- use(使用):从工作内存读取数据来运算
- assign(赋值):将计算好的值重新赋值到工作内存中
- store(存储):将工作内存数据写入主内存
- write(写入):将store过去的变量值赋值给主内存中的变量
- lock(锁定):将主内存变量加锁,标识为线程独占状态
- unlock(解锁):将主内存变量解锁,解锁后其他线程可以锁定该变量
示例代码:
public class MyTest13 {
public static volatile boolean initFlag = false;
public static void main(String [] args) throws Exception {
new Thread(new Runnable() {
@Override
public void run() {
// TODO Auto-generated method stub
System.out.println("run thread1 start");
while(!initFlag) {
}
System.out.println("end run thread1");
}}
).start();
Thread.sleep(200);
new Thread(new Runnable() {
@Override
public void run() {
// TODO Auto-generated method stub
setFlag();
}
}).start();
}
public static void setFlag() {
System.out.println("start set flag");
initFlag = true;
System.out.println("end set flag");
}
}
上述代码中如果initFlag没有被volatile关键字修饰的话,那么整个内存执行流程图如下:
线程1的工作内存永远不会知道initFlag已经被修改。
MESI缓存一致性协议
多个cpu从主内存读取同一个数据到各自的高速缓存,当其中某个cpu修改了缓存里面的数据,该数据会马上同步回主内存,其他cpu通过总线嗅探机制可以感知到数据的变化,从而将自己缓存里的数据失效。如下图
但是上面的缓存一致性协议会有个问题,当线程2工作内存中的数据通过总线时,还没有赋值给主内存中的变量,此时线程1探测到initFlag的改变,马上去主内存中获取数据,那么获取的数据还是没有更新的数据。
volatile缓存可见性实现原理
底层实现主要是通过汇编lock前缀指令,它会锁定这块内存区域的缓存并回写到主内存,此操作被称为缓存锁定,MESI缓存一致性协议机制会阻止同时修改被两个以上处理器缓存的内存区域数据。一个处理器的缓存值通过总线回写到内存会导致其他处理器相应的缓存失效。
并发编程的三大特性:可见性、原子性、有序性
volatile保证可见性与有序性,但是不保证原子性,保证原子性需要借助synchronized锁机制