单线程规则:同一个线程中的每一个操做都happens-before于出如今其后的任何一个操做。app
对一个监视器的解锁操做happens-before于每个后续对同一个监视器的加锁操做。函数
对volatile字段的写入操做happens-before于每个后续的对同一个volatile字段的读操做。线程
Thread.start()的调用操做会happens-before于启动线程里面的操做。code
一个线程中的全部操做都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操做时可见。class
为了更加深刻的了解这些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.