Java 内存模型试图屏蔽各种硬件和操作系统的内存访问差异,以实现让 Java 程序在各种平台下都能达到一致的内存访问效果。
1、主内存与工作内存
处理器上的寄存器的读写的速度比内存快几个数量级,为了解决这种速度矛盾,在它们之间加入了高速缓存。
加入高速缓存带来了一个新的问题:缓存一致性。如果多个缓存共享同一块主内存区域,那么多个缓存的数据可能会不一致,需要一些协议来解决这个问题。
![](https://img2020.cnblogs.com/blog/1757082/202102/1757082-20210218220005326-59595997.png)
所有的变量都存储在主内存中,每个线程还有自己的工作内存,工作内存存储在高速缓存或者寄存器中,保存了该线
程使用的变量的主内存副本拷贝。
线程只能直接操作工作内存中的变量,不同线程之间的变量值传递需要通过主内存来完成。
![](https://img2020.cnblogs.com/blog/1757082/202102/1757082-20210218220051148-333653714.png)
二、内存间交互操作
Java 内存模型定义了 8 个操作来完成主内存和工作内存的交互操作。
![](https://img2020.cnblogs.com/blog/1757082/202102/1757082-20210218220143773-1957657421.png)
- read:把一个变量的值从主内存传输到工作内存中;
- load:在 read 之后执行,把 read 得到的值放入工作内存的变量副本中;
- use:把工作内存中一个变量的值传递给执行引擎;
- assign:把一个从执行引擎接收到的值赋给工作内存的变量;
- store:把工作内存的一个变量的值传送到主内存中;
- write:在 store 之后执行,把 store 得到的值放入主内存的变量中;
- lock:作用于主内存的变量;
- unlock
三、内存模型三大特性
- 原子性:AtomicXxxx类,synchronized;
- 可见性:volatile,synchronized,final;
- 有序性:volatile,synchronized;
名词解释:
- 可见性:指当一个线程修改了共享变量的值,其它线程能够立即得知这个修改。
- 有序性:指在本线程内观察,所有操作都是有序的。在一个线程观察另一个线程,所有操作都是无序的,无序是因为发生了指令重排序。在 Java 内存模型中,允许编译器和处理器对指令进行重排序,重排序过程不会影响到单线程程序的执行,却会影响到多线程并发执行的正确性。
- volatile并不能保证原子性,故可能是不安全的。
上面提到了可以用 volatile 和 synchronized 来保证有序性。除此之外,JVM 还规定了先行发生原则,让一个操作无
需控制就能先于另一个操作完成。
1. 单一线程原则
2. 管程锁定规则
3. volatile 变量规则
4. 线程启动规则
5. 线程加入规则
6. 线程中断规则
7. 对象终结规则
8. 传递性
以下分别图示:
1、单一线程原则 :
在一个线程内,在程序前面的操作先行发生于后面的操作。
![](https://img2020.cnblogs.com/blog/1757082/202102/1757082-20210218221056415-437969602.png)
2. 管程锁定规则 :
一个 unlock 操作先行发生于后面对同一个锁的 lock 操作。
![](https://img2020.cnblogs.com/blog/1757082/202102/1757082-20210218221120706-1898848036.png)
3. volatile 变量规则
对一个 volatile 变量的写操作先行发生于后面对这个变量的读操作。
![](https://img2020.cnblogs.com/blog/1757082/202102/1757082-20210218221214215-1958509904.png)
4. 线程启动规则
Thread 对象的 start() 方法调用先行发生于此线程的每一个动作。
![](https://img2020.cnblogs.com/blog/1757082/202102/1757082-20210218221230074-1835673003.png)
5. 线程加入规则
Thread 对象的结束先行发生于 join() 方法返回。
![](https://img2020.cnblogs.com/blog/1757082/202102/1757082-20210218221248489-1279246766.png)
6. 线程中断规则
对线程 interrupt() 方法的调用先行发生于被中断线程的代码检测到中断事件的发生,可以通过 interrupted() 方法检测到是否有中断发生。
7. 对象终结规则
一个对象的初始化完成(构造函数执行结束)先行发生于它的 fifinalize() 方法的开始。
8. 传递性
如果操作 A 先行发生于操作 B,操作 B 先行发生于操作 C,那么操作 A 先行发生于操作 C。
Over......