最終の「Javaの並行プログラミングの芸術」

最後の並べ替えルール

最終的な最終的な読み書き並べ替え規則を説明するための例として、次のコードで

public FinalExample{
    int a;
    final int b;
    private static FinalExample self;
    private FinalExample(){
        this.a = 1;
        this.b = 5;
    }
    
    public static FinalExample init(){ //线程A执行
        self = new FinalExample();
    }

    public static void read(){ //线程B执行
        FinalExample temp = self; // 获取引用
        int resultA = temp.a; // 读普通域
        int resultB = temp.b; // 读final域
    }
}

最終書き込み並べ替えルール

  • ないソートは、JMMの保証は、最終的な変数を書か外落胆コンストラクタ関数をコンパイルします
  • 最後のフィールドを書き込みますコンパイラは、コンストラクタはStoreStoreを挿入する前に障壁を返します。

次のように)は、Aが(INITを実行するスレッドと仮定して)、スレッドBは、(読み取り、実行、実行の順序であってもよいです。

スレッドが初期化されるとき、スレッドBがゼロに通常時刻を読み出し、その後の実行中ときドメインを取得することができるようにコンストラクタに共通のドメインは、外側が並び替え初期化かもしれないコンストラクタ、および値これは、指定された値1になります。
コンパイラは、最終的なドメインに遭遇したとき、それは後から機能の建設の終了前に書かれた最終の実施を確保するために、最終StoreStoreメモリの障壁に参加しました書き込みます。

最後のフィールドは、読んで、最終的な変数が正しく初期化されている任意のスレッドを保証することができます。

最終的読書の再照合

書き込みに焦点を当て、最終書き込み並べ替えルールについて話したとき。読書の最後のフィールドにもいくつかの特別な処理を行って。典型的には、オブジェクトおよび通常の並べ替えが発生する可能性があるオブジェクト読み取り領域への参照を取得します。したがって、次のような実行の順序が発生することがあります。

()メソッドを読んresultAを書くことが一時変数に依存しているようですので、一見の並べ替えで思わ発生しません。resultAは実際には、一時変数内の参照に依存している間接的に依存するオブジェクトの一時一部のプロセッサは、間接的に依存並べ替えされませんが、場合に、アルファ・プロセッサとして全く欠如が存在しないが、JMMは、この並べ替えを回避することであるだろうに間接的に依存するプロセッサには、次の並べ替えの最終的な読み取りに追加されますルール:

  • JMM並べ替えされない最初のオブジェクト参照最終読み取り、オブジェクトドメインの最初の読み取りを確実に
  • コンパイラインサートは、読み取り領域の前に、最終的な障壁をLoadLoad

最後のフィールドは、参照型であります

最終的な参照オブジェクトのメンバのコンストラクタに書き込まれ、その後、コンストラクタが参照変数に割り当てられた物体の外形を基準とし、これら二つの動作は並べ替えることができません。

final引用不能从构造函数内“溢出”

写final的重排序规则虽然保证了,final域的写会在构造函数执行之前完成,并对其他线程可见。但是如果在构造函数内,引用就发生了溢出,那么就无法保证了

public EscapeFinalExample{
    int a;
    final int b;
    private static EscapeFinalExample self;
    private EscapeFinalExample(){
        this.a = 1;
        this.b = 5;
        self = this; // this引用溢出
    }
    
    public static EscapeFinalExample init(){ //线程A执行
        new EscapeFinalExample();
    }

    public static void read(){ //线程B执行
        EscapeFinalExample temp = self; // 获取引用
        int resultA = temp.a; // 读普通域
        int resultB = temp.b; // 读final域
    }
}

假设线程A执行init(),线程B执行read(),这里的A线程还未完成完整的初始化方法,对象引用就被B可见了。即使代码上 this溢出操作放在最后,仍然有可能被重排序。它们的执行时序如下所示:

上图可以看出,构造函数还没有完成时,final域对其他线程不可见。只有在完成了构造函数后,final域才对其他线程可见。

final语义的特殊例子

在X86处理器上,由于不会发生写写、读写、读读的重排序,所以没有StoreStore内存指令,故在使用final时,编译器会忽略StoreStore内存屏障,同样LoadLoad内存屏障也会被忽略。也就是说,在x86处理器上,final是不做任何处理的。

为什么要增强final语义呢

一方面是final本身是不可修改的,其他线程不该看到final的变化。比如一开始线程读取final值为默认值0,过一段时间再读这个final变量,final值变为值1(被初始化后)。
所以新的模型就保证了,只要正确的完成构造函数(不发生this溢出),即使不用同步,也可以保证其他线程见到final初始化后的值。

おすすめ

転載: www.cnblogs.com/codeleven/p/10963065.html