目录
1.什么是Java内存模型?它和JVM内存模型有什么区别?
2.Java内存模型有哪些组成部分?
3.volatile关键字的作用?
4.什么是指令重排序
5.指令重排序必须满足什么条件
6.指令重排序会对多线程造成影响吗
7.什么是happens-before关系
8.happens-before和as-if-serial语义之间的区别
9.happens-before规则
8.双重检查加锁下的重排序问题
Java内存模型的作用是控制Java线程之间的通信,它决定了一个线程对共享变量的写入什么时候对另一个线程可见;而JVM内存模型指的是Java虚拟机运行时的内存分区。
简单地讲,Java内存模型约定线程之间共享的变量存储在主内存中,而每个线程又有自身私有的本地内存(工作内存),本地内存是Java线程直接能读/写到的区域。
重排序是指编译器和处理器为了优化程序性能而对指令序列的执行顺序进行重新排列的一种手段。
double a = 1.0; // 1
double b = 2.0; // 2
double result = a * b; // 3
经过指令重排序后,可能被优化成以下:
double b = 2.0; // 2
double a = 1.0; // 1
double result = a * b; //3
1和2的执行顺序被调换,但是不会影响单线程下,执行3获得的结果。
【数据依赖性:如果两个操作同时访问一个变量,且这两个操作中有一个为写操作,那么这两个操作具有数据依赖性。】
double a = 1.0; // 1
double b = a * 2.0; // 2
double result = a * b;
例如上述代码,不会被重排序调换1和2的位置,因为这两条指令都对a进行了操作,且1对a进行了写操作,存在数据依赖性。
会。因为指令重排序只保证在单线程下的结果的一致性,但是在多线程下,若共享变量不存在数据依赖性,那么可能会造成不一样的结果,如以下代码:
int a = 0;
boolean flag = false;
public void writer() {
a = 1; // 1
flag = true; // 2
}
public void reader() {
if (flag) { // 3
int i = a * a; // 4
System.out.println("读取到的a为" + a);
}
}
正常顺序执行过程,应该是1->2->3->4,读取到的a为1; 在上述代码中,由于a和flag不存在数据依赖性,所以指令1和2可能会被重排序。
在单线程环境下,由于as-if-serial语义,虽然执行顺序变成了2在1前面,但是仍然会得到一样的结果:
2->1->3->4,读取到的a为1
在多线程环境下,可能会发生以下问题:
线程A执行writer(): 2-> ->1
线程B执行reader(): 3->4 ,读取到的a为0
上述问题,只需要在a和b之前使用volatile修饰就可以解决了,因为使用volatile可以避免指令的重排序。那么执行顺序只能是1->2->3->4。
happens-before关系即先行发生关系,它提供了线程操作可见性的保证。
happens-before有如下规则用以保证它的先行发生可见性:
一个错误的双重检查加锁Demo
public class Singleton {
private Singleton instance = null;
private Singleton() {}
public Singleton getInstance() {
// 先判断对象是否已经初始化
if (instance == null) {
synchronized (instance) {
// 确定对象没有初始化
if (instance == null) {
instance = new Singleton();
}
}
}
return instance;
}
}
上述代码发生错误的原因在于return new Singleton()
这行代码,这行代码并不是一个原子性的的操作,它可以分为以下三个阶段:
1.分配对象内存
2.初始化对象
3.将对象引用到内存空间
其中,在将对象引用到内存空间时,instance就不等于null了。
在单线程情况下,2和3指令发生重排序,但是由于as-if-serial语义,其执行结果也是不变的:
在多线程情况下,有可能会发生以下的执行顺序,导致程序执行结果出错: A线程在执行到第3步的时候即设置instance指向内存空间,此时B线程判断其不为空,返回instance,但此时instance并未进行初始化,所以出现了错误,返回了一个空对象。
解决该问题的方法是在instance前加上volatile修饰,这样就能禁止重排序。