-
首先理清 CPU,缓存,内存,IO 之间的关系。
-
为什么需要缓存?缓存是从CPU中划分出来,为了均衡CPU和内存之间的速度。他们之间的速度: CPU>内存>IO。
-
什么是Java内存模型?
-
Java内存模型是一种规范,规范了JVM 如何 按需 禁用缓存,禁用编译器的优化操作。
-
Java内存模型中的3个重要概念 ,可见性,原子性,有序性。
-
什么是可见性?怎么保证可见性?
-
在单核的CPU时代,所有的线程都是在 操作同一个CPU的缓存。 一个线程 对缓存中的写,对另一个线程一定是可见的。
-
在多核CPU时代,每次CPU都有自己的缓存。 多个线程操作的就是多个不同的CPU。 A线程在CPU1上对共享变量的操作对作用在CPU2上的线程B 是不可见。这里要注意的是, 线程 只是在某1个CPU上执行任务,但是一个CPU可以有多个线程存在。
-
可见性: 一个线程对共享变量的修改对另一个线程是可见的。
-
怎么保证可见性:所有线程直接在内存上对共享变量进行读写,保证了共享变量在多个线程之间是可见的。可以直接用volatile 修改共享变量,告诉编译器对数据读和写直接在内存中操作。
-
什么是有序性?怎么保证有序性?
-
有序性是指编译器为了优化性能,有时候可能会该改变程序语句的先后顺序。
-
保证有序性主要是禁止编译器进行指令重排。可以使用volatile来修饰共享变量,在编译器生成字节码时,会在volatile写操作的前面和后面,分别加入了 内存屏障,防止指令重排。
-
原子性是什么?怎么保证原子性?
-
原子性是 一个或多个操作在CPU执行过程中不被中断的特征就叫原子性。
-
Java 作为高级语言,高级语言中的一个语句,需要多条CPU指令来执行。
-
比如count+=1这条语句需要3条CPU指令。 首先 将count的值从内存加载到寄存器中。然后 在寄存器中执行+1的操作。最后 将寄存器中的结果写到内存中。但是CPU每执行完一条指令(Java的一条程序语句 就包含多条指令)就会进行CPU的切换。就会导致导致原子性的问题。
-
解决原子性:保证同一时刻只有一个线程能对共享变量进行修改。通过sychronized对临界资源进行保护,就可以保证原子性。
-
最后总结一下volatile,final ,sychronized 保证了原子性,volatile和sychronized 保证了有序性,而 原子性 通过sychronized 可以保证原子性。