読み取りと書き込みの最後のフィールドは、より一般的な変数へのアクセスのようなものです
最終規則の並べ替えドメイン
最終的なドメイン、コンパイラおよびプロセッサは、2つのリオーダーのルールを遵守するため。
(1)最後のフィールドのコンストラクタに書き込まれ、その後、参照されたオブジェクトを参照変数に割り当てられているこの構成は、並べ替えは、これら2つの操作の間はありません。
(2)最終的な参照を含む第一の標的ドメインを読み取り、次いで、最終的な最初のフィールドを読み取るには、並べ替えは、これら2つの操作の間はありません。
例
public class FinalExample {
int i;普通变量
final int j;final变量
static FinalExample obj;
public FinalExample() { 构造函数
i = 1; 写普通域
j = 2; 写final域
}
public static void writer() { 写线程A执行
obj = new FinalExample();
}
public static void reader() { 读线程B执行
FinalExample object = obj; 读对象引用
int a = object.i; 读普通域
int b = object.j; 读final域
}
}
ライタースレッドAの実行を仮定する(他のスレッドBに続く)この方法は、リーダー()メソッドを実行します。
並べ替えルールに、最終的なドメインを書きます
書かれた最後のフィールドの並べ替え規則は、コンストラクタの外に最後のフィールドを並べ替えの書き込みを禁止しています。
(1)JMMはリオーダー最終フィールド外部コンストラクタを書き込むためのコンパイラを防ぎます。
コンパイラの前に(2)StoreStore障壁を挿入し、最後のフィールドの後にコンストラクタリターンを書き込みます。この障壁は、コンストラクタの外に最後のフィールドを並べ替え書き込むためのプロセッサを禁止されています。
ライター()メソッド
タイプFinalExampleのオブジェクトを構築する(1)。
(2)オブジェクト参照変数objに参照によって割り当て。
ことを確実にするためのルールを並べ替え書かれた最後のフィールド:
見て、最終的にドメインオブジェクトの前に任意のスレッドへの参照が正しくオブジェクトの上に初期化されています。
このドメインは、通常のセキュリティスレッドBは、オブジェクト参照OBJ、おそらくまだ構築されていないオブジェクトobj(通常のドメインの私の書き込みを外部コンストラクタ、さらに初期値を並べ替えているとき「を参照してください。」持っていません一般的なドメインは、私が書かれていません)
ドメインを並べ替え最終規則を読みます
スレッドで、リードフィールド最終規則を並べ替え、それは、最初のドメインの最初の読み出し参照シオン最終読み取りあり、オブジェクトは、これら2つの操作を並べ替えJMM禁止プロセッサを含んでいます。LoadLoadコンパイラは、最終的な読み取りドメイン操作の前にバリアを挿入します。
最初の読み出し及び初期読み取り最終オブジェクト参照フィールドは、オブジェクト、二つの操作の間に間接的な依存関係を含んでいます。コンパイラー準拠間接的な依存関係するので、コンパイラは、これら二つの動作の並べ替えを行います。
Reder()メソッドは、3つの操作含む
(1)初期読み出し基準変数OBJ
(2)オブジェクト通常ドメインJポインティングOBJ初期読み出し基準変数を
(3)iはオブジェクトの最終フィールドポインティングOBJ最初の読み出し基準変数
ライタースレッドAを想定が発生していません任意の並べ替え、およびプログラムの実行間接的にプロセッサに依存準拠していない
通常のドメインオブジェクトを読み取るための操作は、オブジェクト参照を読み取るためにプロセッサの前に並べ替えています。読書は、一般的なドメインで、ドメインが書かれたスレッドAが書き込まれていない、これは間違った読み出し動作です。最終的な読み出し動作フィールドルールは最終的なスレッドAドメインが初期化された時点でオブジェクト参照を読んだ後、「定義」のリオーダー最終ターゲットドメインを、読み取りし、これは、適切な読み出し動作であるが
、最終的なフィールドを読み取ります並べ替え規則は確実:対象ドメインの最後の読み取りの前に、我々は最初の最後のフィールドは、オブジェクトの参照が含まれて読み込みます。参照がnullでない場合は、この例では、そのオブジェクトの参照最終ドメインは、前のスレッドを初期化されている必要があります。
最後のフィールドは、参照型であります
public class FinalReferenceExample {
final int[] intArray; final是引用类型
static FinalReferenceExample obj;
public FinalReferenceExample() { 构造函数
intArray = new int[1]; 1
intArray[0] = 1; 2
}
public static void writerOne() { 写线程A执行
obj = new FinalReferenceExample(); 3
}
public static void writerTwo() { 写线程B执行
obj.intArray[0] = 2; 4
}
public static void reader() { 读线程C执行
if (obj != null) { 5
int temp1 = obj.intArray[0]; 6
}
}
}
参照型のため、書き込み磁界最終並べ替えは、コンパイラを支配し、プロセッサは、次の制約を追加します。
最終的な参照オブジェクトのコンストラクタ内のドメインのメンバーを書き込み、これは、オブジェクトのコンストラクタ外側に構成されています変数に割り当てられた参照を参照し、次の2つの間でリオーダ操作できない
リーダースレッドが最低書き込みC最終的にドメイン・オブジェクト参照のメンバーに書き込まれたコンストラクタ内のスレッドで見ることができることがJMMの性を保証します。
アレイ内の少なくとも参照するに、すなわち0 C 1標識されています。ライター・スレッドBは筆記配列要素が表示される場合があります、読み取りスレッドC、見ることができませんでした。
JMMは、データの読み出しと書き込みスレッドBスレッドCは、この時点での結果は予測不能である間の競争があるため、可視書き込みスレッドBはスレッドCを読み取ることが保証されません。
あなたは、読み取り、書き込み、スレッドBスレッドが書き込み、書き込みに配列要素を参照してくださいCを確認するには、メモリはスレッドの可視性を読んでいることを保証するために、スレッドBとCの間で同期プリミティブ(ロックまたは揮発性)を使用する必要があります。
あなたは、最終的な参照コンストラクタから逃れることができません
いずれかのスレッドが、最終的なオブジェクト変数領域の基準点として参照変数で見ることができる前に、正しくコンストラクタで初期化されている:書かれた最後のフィールドの並べ替え規則は、ことを確認します。この効果を得るために、あなたも保証を必要とする:内部では、コンストラクタ、もはやオブジェクト参照のコンストラクタエスケープで見ることができ、他のスレッドのために、この構造化オブジェクト参照を、作るために飛び込みました。
public class FinalReferenceEscapeExample {
final int i;
static FinalReferenceEscapeExample obj;
public FinalReferenceEscapeExample() {
i = 1; 1写final域
obj = this; 2 this引用在此"逸出"
}
public static void writer() {
new FinalReferenceEscapeExample();
}
public static void reader() {
if (obj != null) { 3
int temp = obj.i; 4
}
}
}
スレッドAの実行wirter()メソッド、Bリーダー()メソッドを実行する他のスレッドを考えます。2操作オブジェクトはここで、スレッドBの前に目に見えるように構成されているように完了していません。2ここでの操作は、コンストラクタの最後のステップ、および操作1の方法の背後にある2行の操作スレッドの場合でも、(読み取り実行)あなたが運転ここと1ので、ピットはまだ、プログラムの最後のフィールドの値を初期化することを見ることができません動作は2との間に並べ替えることができる
参照されるオブジェクトは、ドメインが初期化されていないため、最終的な構成の他のスレッドのためには見られないコンストラクタが戻る前に。コンストラクタ戻った後、任意のスレッドは、最終的なフィールドが正しく初期化後の値を見ることが保証されます。
JSR-133は、セマンティック決勝を強化しました
古いJavaのメモリモデルでは、最も深刻な欠陥の一つは、スレッドが最終フィールドの値が変更されます表示される場合があります。
(スレッドが現在最後のフィールドは(デフォルト値が前に初期化されていない)0に設定されている整数を見ているとき例えば、行く時間の経過後に、このフィールドのこのスレッド最終値を読みますが、値を1に見つかりました初期化後のスレッド値)。
最も一般的な例は、古いJavaのメモリモデルで、文字列の値が変更されることがあります。
この脆弱性を修正するには、JSR-133専門家グループは、セマンティック最終的に向上させます。
最後の読み書きや並べ替えルールのための場を増やすことによって、初期化は、Javaプログラマのためのセキュリティを提供することができます:オブジェクトは限りが長い適切に構築される(コンストラクタ構築されたオブジェクトには、「エスケープ」を引用していない)、あなたが同期使用する必要はありません値(ロック及び揮発性の使用を意味する)は、任意のスレッドが最後のフィールドは、コンストラクタの後に初期化されて見ることができるようにすることができ