JSR133给Java内存模型定义的happen-before规则

  1. 单线程规则:同一个线程中的每个操作都happens-before于出现在其后的任何一个操作。

  2. 对一个监视器的解锁操作happens-before于每一个后续对同一个监视器的加锁操作。

  3. 对volatile字段的写入操作happens-before于每一个后续的对同一个volatile字段的读操作。

  4. Thread.start()的调用操作会happens-before于启动线程里面的操作。

  5. 一个线程中的所有操作都happens-before于其他线程成功返回在该线程上的join()调用后的所有操作。

  6. 一个对象构造函数的结束操作happens-before与该对象的finalizer的开始操作。

  7. 传递性规则:如果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.


你可能感兴趣的:(synchronized,内存模型,Happen-Before)