单线程规则:同一个线程中的每个操作都happens-before于出现在其后的任何一个操作。
对一个监视器的解锁操作happens-before于每一个后续对同一个监视器的加锁操作。
对volatile字段的写入操作happens-before于每一个后续的对同一个volatile字段的读操作。
Thread.start()的调用操作会happens-before于启动线程里面的操作。
一个线程中的所有操作都happens-before于其他线程成功返回在该线程上的join()调用后的所有操作。
一个对象构造函数的结束操作happens-before与该对象的finalizer的开始操作。
传递性规则:如果A操作happens-before于B操作,而B操作happens-before与C操作,那么A动作happens-before于C操作。
实际上这组happens-before规则定义了操作之间的内存可见性,如果A操作happens-before B操作,那么A操作的执行结果(比如对变量的写入)必定在执行B操作时可见。
为了更加深入的了解这些happens-before规则,我们来看一个例子:
//线程A,B共同访问的代码
Object lock = new Object();
int a=0;
int b=0;
int c=0;
//线程A,调用如下代码
synchronized(lock){
a=1; //1
b=2; //2
} //3
c=3; //4
//线程B,调用如下代码
synchronized(lock){ //5
System.out.println(a); //6
System.out.println(b); //7
System.out.println(c); //8
}
我们假设线程A先运行,分别给a,b,c三个变量进行赋值(注:变量a,b的赋值是在同步语句块中进行的),然后线程B再运行,分别读取出这三个变量的值并打印出来。那么线程B打印出来的变量a,b,c的值分别是多少?
根据单线程规则,在A线程的执行中,我们可以得出1操作happens before于2操作,2操作happens before于3操作,3操作happens before于4操作。同理,在B线程的执行中,5操作happens before于6操作,6操作happens before于7操作,7操作happens before于8操作。而根据监视器的解锁和加锁原则,3操作(解锁操作)是happens before 5操作的(加锁操作),再根据传递性 规则我们可以得出,操作1,2是happens before 操作6,7,8的。
则根据happens-before的内存语义,操作1,2的执行结果对于操作6,7,8是可见的,那么线程B里,打印的a,b肯定是1和2. 而对于变量c的操作4,和操作8. 我们并不能根据现有的happens before规则推出操作4 happens before于操作8. 所以在线程B中,访问的到c变量有可能还是0,而不是3.
来源:oschina
链接:https://my.oschina.net/u/1169535/blog/652285