1. happens-before简介
JMM中,如果一个操作执行的结果需要对另一个操作可见,那么这两个操作之间必须要存在happens-before关系。两个操作可以在同一个线程中,也可以不在
常见的 happens-before关系:
- 程序顺序规则 : 一个线程的一个操作发生于其后续操作前
- 监视器锁规则: 对一个锁(对象)的解锁发生在加锁之前
- volatile变量规则:对一个volatile变量的写操作发生在读操作前
- 传递性:A在B前面发生,B在C前面发生=>A在C前面发生
2. 数据依赖性
数据依赖有以下3种情况:
- 写后读:
a=1; b=a - 写后写:
a=1;a=2 - 读后写:
a=b;b=1
上面的这三种情况,每种情况中的语句顺序都不以改变,不然会改变程序运行的结果
3. 重排序
Java 内存模型允许编译器和处理器对指令重排序 以提高运行性能 ,并且只会对不存在 数据依赖性的指令重排序。 在单线程下重排序可以保证最终执行的结果与程序顺序执行的 结果一致,但是在多线程下就会存在问题
package innerlock;
public class ReorderDemo {
int a=0;
boolean flag=false;
public void write() {
a=1;
flag=true;
}
public void read() {
if(flag)
{
int i=a*a;
System.out.println("i="+i);
}
}
public static void main(String[] args) throws InterruptedException {
ReorderDemo demo=new ReorderDemo();
Thread wThread=new Thread() {
public void run() {
demo.write();
}
};
Thread rThread=new Thread() {
public void run() {
while(!Thread.currentThread().isInterrupted())
{
demo.read();
}
}
};
wThread.start();
rThread.start();
Thread.sleep(2000);
rThread.interrupt();
}
}
上面的代码先创建了一个写线程,然后创建了一个读线程,按照常规想法,结果应该会输出i=1, 因为flag的赋值操作在a=1之后执行
但是由于1处和2处的代码没有数据依赖性,3处和4处的代码也没有数据依赖性,因此1与2,3与4可能会发生指令重排而导致结果不一样
1处与2处指令重排: 3处与4处指令重排:
3处与4处存在控制依赖性
参考:《Java并发编程的艺术》
|