背景
我们知道导致cpu缓存导致了可见性问题,编译器优化带来了有序性问题。那么如果我们禁用了cpu缓存与编译器优化,就能够解决问题,但是性能就无法提升了。所以一个合理的方案,就是按照一定规范来禁用缓存和编译器优化,即在某些情况下禁用缓存与编译器优化。Java内存模型就是这样的一个规范,用来解决可见性与有序性问题
概念
java内存模型本质上就是规范了JVM如何按照规则禁用缓存和编译器优化,既面向应用开发人员,也面向jvm的实现。这些规范包括了volatile、synchronized和final三个关键字,以及六项Happens-Before规则。
volatile
volatile实质就是告诉编译器,对volatile修饰的变量,不能使用cpu缓存,必须从内存中读取或者写入。
Happens-Before规则,简称为HB
HB本质是一种可见性,即前面一个操作结果对后面的操作是可见的,换句话说就是后面的操作能够看见前面的操作结果。HB约束了编译器的优化行为,允许优化,但是优化后的结果要符合HB规则
同一线程顺序性规则
这条规则是指在一个线程中,按照程序顺序,前面的操作HB于后续的操作。
volatile变量规则
这条规则是指对一个 volatile 变量的写操作, HB于后续对这个变量的读操作。
传递性
这条规则是指A HB于 B,B HB于 C,那么A HB 于C。
根据以上三条格则继续看下面的例子
public class HappensBeforeDemo { int x = 0; volatile boolean v = false; public void write(){ x = 42; v = true; } public void read(){ if(v){ //x为多少呢 } } }
当线程A执行完write方法,线程B执行read方法后,读到了v为true后,x是多少呢?首先在线程A中,根据同一线程顺序性规则,x=42 HB与v=true;再根据volatile变量规则,v=true的写操作HB于v的读操作;再根据传递性规则,x=42 HB于v的读操作,所以线程B中的值一定为42。在java并发包中的工具类,正是依靠这三条规则来实现可见性的
synchronized锁规则
这条规则指对一个锁的解锁操作HB于后续对这个锁的加锁加锁操作
线程start()规则
主线程A启动子线程B后,子线程B能够看到主线程在启动子线程之前的操作结果
线程join规则
在主线程A中等待子线程B完成(主线程A通过调用子线程B的join方法实现),当子线程完成后(主线程中的join方法返回),主线程能够看到子线程的操作结果
原文地址:https://www.cnblogs.com/hello---word/p/10992069.html