someObject
別のスレッドが「構築中に」変数をチェックした場合、(メモリモデルの癖により) 部分的に初期化されたオブジェクトが表示される可能性があると思います。新しい (Java 5 の) メモリ モデルは、オブジェクトが他のスレッドから見えるようになる前に、すべてのfinalフィールドをその値に設定する必要があることを意味します (ただし、新しく作成されたオブジェクトへの参照が他のスレッドのコンストラクタからエスケープされない限り)。方法) しかし、それ以上の保証はあまりありません。
基本的に、適切なロックなしでデータを共有しないでください (または静的初期化子などによって与えられる保証) :) 真剣に、一般的なロックフリー プログラミングと同様に、メモリ モデルは非常にトリッキーです。これが可能になるのを避けるようにしてください。
論理的には、代入はコンストラクターの実行後に発生します。そのため、同じスレッドから変数を観察すると、コンストラクターの呼び出し中に null になります。ただし、私が言うように、メモリ モデルには奇妙な点があります。
編集: ダブルチェック ロックの目的で、フィールドがJava 5 以降を使用している場合volatile
は、これを回避できます。Java 5 より前のメモリ モデルは、これに対して十分に強力ではありませんでした。ただし、パターンを正確に正しく取得する必要があります。詳細については、Effective Java、第 2 版、項目 71 を参照してください。
編集:アーロンのインライン化が単一のスレッドで表示されることに反対する私の理由は次のとおりです。次があるとします。
public class FooHolder
{
public static Foo f = null;
public static void main(String[] args)
{
f = new Foo();
System.out.println(f.fWasNull);
}
}
// Make this nested if you like, I don't believe it affects the reasoning
public class Foo
{
public boolean fWasNull;
public Foo()
{
fWasNull = FooHolder.f == null;
}
}
これは常に報告されると思いますtrue
。セクション 15.26.1から:
それ以外の場合は、次の 3 つの手順が必要です。
- 最初に、左側のオペランドが評価されて変数が生成されます。この評価が突然完了すると、代入式は同じ理由で突然完了します。右側のオペランドは評価されず、代入は行われません。
- それ以外の場合は、右側のオペランドが評価されます。この評価が突然完了すると、代入式は同じ理由で突然完了し、代入は発生しません。
それ以外の場合、右側のオペランドの値は左側の変数の型に変換され、適切な標準値セット (拡張指数値セットではない) への値セット変換 (§5.1.13) を受けます。変換の結果は変数に格納されます。
次に、セクション 17.4.5から:
2 つのアクションは、先行発生関係によって順序付けできます。あるアクションが別のアクションの前に発生する場合、最初のアクションは 2 番目のアクションよりも前に表示され、順序付けされます。
x と y の 2 つのアクションがある場合、hb(x, y) と書き、x が y の前に発生することを示します。
- x と y が同じスレッドのアクションであり、プログラムの順序で x が y の前にある場合、hb(x, y) になります。
- オブジェクトのコンストラクターの最後から、そのオブジェクトのファイナライザー (§12.6) の開始までの先行発生エッジがあります。
- アクション x が後続のアクション y と同期する場合、hb(x, y) も得られます。
- hb(x, y) と hb(y, z) の場合、hb(x, z) です。
2 つのアクション間に事前発生関係が存在するからといって、必ずしも実装でその順序で実行する必要があるとは限らないことに注意してください。並べ替えが合法的な実行と一致する結果をもたらす場合、それは違法ではありません。
つまり、単一のスレッド内でも奇妙なことが起こっても問題ありませんが、それが観察可能であってはなりません。この場合、違いは観察可能であるため、違法であると私は信じています。