12

Java 言語仕様では、セクション 17.5で final フィールドのセマンティクスを定義しています。

final フィールドの使用モデルは単純なものです。オブジェクトのコンストラクターでオブジェクトの最終フィールドを設定します。オブジェクトのコンストラクターが終了する前に、別のスレッドが参照できる場所に構築中のオブジェクトへの参照を書き込まないでください。これに従えば、オブジェクトが別のスレッドから見られるとき、そのスレッドは常に、そのオブジェクトの final フィールドの正しく構築されたバージョンを認識します。また、少なくとも最終フィールドと同じくらい最新の最終フィールドによって参照されるオブジェクトまたは配列のバージョンも表示されます。

私の質問は - 「最新」の保証は、ネストされた配列とネストされたオブジェクトの内容にまで及びますか?

簡単に言うと、1 つのスレッドが可変オブジェクト グラフをオブジェクトの final フィールドに割り当て、オブジェクト グラフが更新されない場合、すべてのスレッドが final フィールドを介してそのオブジェクト グラフを安全に読み取ることができますか?

シナリオ例:

  1. スレッド A は ArrayLists の HashMap を構築し、その HashMap をクラス「MyClass」のインスタンスの最終フィールド「myFinal」に割り当てます。
  2. スレッド B は、MyClass インスタンスへの (非同期の) 参照を確認し、「myFinal」を読み取り、ArrayLists の 1 つの内容にアクセスして読み取ります。

このシナリオでは、スレッド B から見た ArrayList のメンバーは、少なくとも MyClass のコンストラクターが完了したときと同じくらい最新であることが保証されていますか?

同期などの代替ソリューションではなく、Java メモリ モデルと言語仕様のセマンティクスの明確化を求めています。私の夢の答えは、関連するテキストを参照して、はいまたはいいえです。

アップデート:

  • Java 1.5 以降のセマンティクス、つまり JSR 133 で導入された更新された Java メモリ モデルに興味があります。最終フィールドの「最新」の保証は、この更新で導入されました。
4

2 に答える 2

7

このシナリオでは、スレッド B から見た ArrayList のメンバーは、少なくとも MyClass のコンストラクターが完了したときと同じくらい最新であることが保証されていますか?

はい、そうです。

スレッドは、参照が初めて発生したときにメモリを読み取る必要があります。ハッシュ マップが構築されるため、その中のすべてのエントリが真新しく、オブジェクトへの参照はup-to-date、コンストラクタが終了したときのものになります。

その最初の遭遇の後、通常の可視性ルールが適用されます。そのため、他のスレッドが最終参照の非最終フィールドを変更すると、他のスレッドはその変更を認識しない場合がありますが、コンストラクターからの参照は引き続き表示されます。

実際には、コンストラクターの後に最終的なハッシュ マップを変更しない場合、その内容はすべてのスレッドで定数になることを意味します。

編集

私はこの保証をどこかで見たことがあることを知っていました.

JSR 133 について説明しているこの記事の興味深い段落を次に示します。

初期化の安全性

新しい JMM は、初期化の安全性を新たに保証することも目指しています。つまり、オブジェクトが適切に構築されている限り (つまり、コンストラクターが完了する前にオブジェクトへの参照が発行されないことを意味します)、すべてのスレッドが値を参照します。あるスレッドから別のスレッドに参照を渡すために同期が使用されているかどうかに関係なく、コンストラクターで設定された最終フィールド。さらに、最終フィールドによって参照されるオブジェクトのフィールドなど、適切に構築されたオブジェクトの最終フィールドを介して到達できる変数は、他のスレッドからも可視であることが保証されています。これは、final フィールドに LinkedList などへの参照が含まれている場合、参照の正しい値が他のスレッドに表示されることに加えて、また、構築時のその LinkedList の内容は、同期なしで他のスレッドに表示されます。その結果、final の意味が大幅に強化されます。つまり、final フィールドは同期せずに安全にアクセスでき、コンパイラは final フィールドが変更されないと想定できるため、複数のフェッチを最適化できます。

于 2010-05-13T23:06:33.697 に答える
1

コンストラクターが次のように記述されている場合、問題はありません。

public class MyClass {
    public final Map myFinal;
    public MyClass () {
        Map localMap = new HashMap();
        localMap.put("key", new ArrayList());
        this.myFinal = localMap;
    }
}

これは、パブリック参照に割り当てられる前にマップが完全に初期化されるためです。コンストラクターが完了すると、最終的な Map が最新になります。

于 2010-05-14T00:19:34.207 に答える